From b7f40ffe14fef7104b63d1f608eb1a2dcad1a40a Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 15 Jan 2025 13:23:13 +0100 Subject: [PATCH 01/97] wip --- .../ObservedSurfaceLayer.ts | 124 +++++++ .../ObservedSurfaceSettingsContext.ts | 137 +++++++ .../ObservedSurfaceLayer/index.ts | 2 + .../ObservedSurfaceLayer/types.ts | 9 + .../RealizationGridLayer.ts | 198 +++++++++++ .../RealizationGridSettingsContext.ts | 176 +++++++++ .../RealizationGridLayer/index.ts | 2 + .../RealizationGridLayer/types.ts | 12 + .../RealizationPolygonsLayer.ts | 121 +++++++ .../RealizationPolygonsSettingsContext.ts | 116 ++++++ .../RealizationPolygonsLayer/index.ts | 2 + .../RealizationPolygonsLayer/types.ts | 9 + .../RealizationSurfaceLayer.ts | 130 +++++++ .../RealizationSurfaceSettingsContext.ts | 152 ++++++++ .../RealizationSurfaceLayer/index.ts | 2 + .../RealizationSurfaceLayer/types.ts | 10 + .../StatisticalSurfaceLayer.ts | 155 ++++++++ .../StatisticalSurfaceSettingsContext.ts | 169 +++++++++ .../StatisticalSurfaceLayer/index.ts | 2 + .../StatisticalSurfaceLayer/types.ts | 13 + .../src/modules/3DViewerNew/interfaces.ts | 24 ++ .../src/modules/3DViewerNew/loadModule.tsx | 13 + frontend/src/modules/3DViewerNew/preview.tsx | 8 + frontend/src/modules/3DViewerNew/preview.webp | Bin 0 -> 52254 bytes .../src/modules/3DViewerNew/registerModule.ts | 29 ++ .../3DViewerNew/settings/atoms/baseAtoms.ts | 8 + .../settings/atoms/derivedAtoms.ts | 19 + .../layerManagerComponentWrapper.tsx | 334 +++++++++++++++++ .../modules/3DViewerNew/settings/settings.tsx | 146 ++++++++ frontend/src/modules/3DViewerNew/types.ts | 4 + .../view/components/LayersWrapper.tsx | 154 ++++++++ .../view/components/ReadoutBoxWrapper.tsx | 117 ++++++ .../view/components/ReadoutWrapper.tsx | 76 ++++ .../3DViewerNew/view/components/Toolbar.tsx | 21 ++ .../customDeckGlLayers/AdvancedWellsLayer.ts | 67 ++++ .../customDeckGlLayers/PlaceholderLayer.ts | 21 ++ .../customDeckGlLayers/WellborePicksLayer.ts | 121 +++++++ .../3DViewerNew/view/utils/layerFactory.ts | 335 ++++++++++++++++++ .../view/utils/makeViewsAndLayers.ts | 183 ++++++++++ .../src/modules/3DViewerNew/view/view.tsx | 24 ++ frontend/src/modules/registerAllModules.ts | 3 +- 41 files changed, 3247 insertions(+), 1 deletion(-) create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/index.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/index.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/index.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/types.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/index.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts create mode 100644 frontend/src/modules/3DViewerNew/interfaces.ts create mode 100644 frontend/src/modules/3DViewerNew/loadModule.tsx create mode 100644 frontend/src/modules/3DViewerNew/preview.tsx create mode 100644 frontend/src/modules/3DViewerNew/preview.webp create mode 100644 frontend/src/modules/3DViewerNew/registerModule.ts create mode 100644 frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts create mode 100644 frontend/src/modules/3DViewerNew/settings/atoms/derivedAtoms.ts create mode 100644 frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx create mode 100644 frontend/src/modules/3DViewerNew/settings/settings.tsx create mode 100644 frontend/src/modules/3DViewerNew/types.ts create mode 100644 frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx create mode 100644 frontend/src/modules/3DViewerNew/view/components/ReadoutBoxWrapper.tsx create mode 100644 frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx create mode 100644 frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx create mode 100644 frontend/src/modules/3DViewerNew/view/customDeckGlLayers/AdvancedWellsLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/view/customDeckGlLayers/PlaceholderLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts create mode 100644 frontend/src/modules/3DViewerNew/view/utils/makeViewsAndLayers.ts create mode 100644 frontend/src/modules/3DViewerNew/view/view.tsx diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts new file mode 100644 index 000000000..7d48713b8 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts @@ -0,0 +1,124 @@ +import { SurfaceDataPng_api, getSurfaceDataOptions } from "@api"; +import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; +import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; +import { FullSurfaceAddress, SurfaceAddressBuilder } from "@modules/_shared/Surface"; +import { SurfaceDataFloat_trans, transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; +import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; +import { QueryClient } from "@tanstack/react-query"; + +import { isEqual } from "lodash"; + +import { ObservedSurfaceSettingsContext } from "./ObservedSurfaceSettingsContext"; +import { ObservedSurfaceSettings } from "./types"; + +export class ObservedSurfaceLayer + implements Layer +{ + private _layerDelegate: LayerDelegate; + private _itemDelegate: ItemDelegate; + + constructor(layerManager: LayerManager) { + this._itemDelegate = new ItemDelegate("Observed Surface", layerManager); + this._layerDelegate = new LayerDelegate( + this, + layerManager, + new ObservedSurfaceSettingsContext(layerManager), + LayerColoringType.COLORSCALE + ); + } + + getSettingsContext() { + return this._layerDelegate.getSettingsContext(); + } + + getItemDelegate(): ItemDelegate { + return this._itemDelegate; + } + + getLayerDelegate(): LayerDelegate { + return this._layerDelegate; + } + + doSettingsChangesRequireDataRefetch( + prevSettings: ObservedSurfaceSettings, + newSettings: ObservedSurfaceSettings + ): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeBoundingBox(): BoundingBox | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return { + x: [data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.max_x], + y: [data.transformed_bbox_utm.min_y, data.transformed_bbox_utm.max_y], + z: [0, 0], + }; + } + + makeValueRange(): [number, number] | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return [data.value_min, data.value_max]; + } + + fetchData(queryClient: QueryClient): Promise { + let surfaceAddress: FullSurfaceAddress | null = null; + const addrBuilder = new SurfaceAddressBuilder(); + + const settings = this.getSettingsContext().getDelegate().getSettings(); + const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); + const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); + const attribute = settings[SettingType.SURFACE_ATTRIBUTE].getDelegate().getValue(); + const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + + if (ensembleIdent && surfaceName && attribute && timeOrInterval) { + addrBuilder.withEnsembleIdent(ensembleIdent); + addrBuilder.withName(surfaceName); + addrBuilder.withAttribute(attribute); + addrBuilder.withTimeOrInterval(timeOrInterval); + + surfaceAddress = addrBuilder.buildObservedAddress(); + } + + const surfAddrStr = surfaceAddress ? encodeSurfAddrStr(surfaceAddress) : null; + + const queryKey = ["getSurfaceData", surfAddrStr, null, "png"]; + + this._layerDelegate.registerQueryKey(queryKey); + + const promise = queryClient + .fetchQuery({ + ...getSurfaceDataOptions({ + query: { + surf_addr_str: surfAddrStr ?? "", + data_format: "png", + resample_to_def_str: null, + }, + }), + }) + .then((data) => transformSurfaceData(data)); + + return promise; + } + + serializeState(): SerializedLayer { + return this._layerDelegate.serializeState(); + } + + deserializeState(serializedState: SerializedLayer): void { + this._layerDelegate.deserializeState(serializedState); + } +} + +LayerRegistry.registerLayer(ObservedSurfaceLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts new file mode 100644 index 000000000..da58be5cf --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts @@ -0,0 +1,137 @@ +import { SurfaceTimeType_api, getObservedSurfacesMetadataOptions } from "@api"; +import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; +import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; +import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; +import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +import { ObservedSurfaceSettings } from "./types"; + +export class ObservedSurfaceSettingsContext implements SettingsContext { + private _contextDelegate: SettingsContextDelegate; + + constructor(layerManager: LayerManager) { + this._contextDelegate = new SettingsContextDelegate( + this, + layerManager, + { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.SURFACE_ATTRIBUTE]: new SurfaceAttributeSetting(), + [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + } + ); + } + + getDelegate(): SettingsContextDelegate { + return this._contextDelegate; + } + + getSettings() { + return this._contextDelegate.getSettings(); + } + defineDependencies({ + helperDependency, + availableSettingsUpdater, + workbenchSession, + queryClient, + }: DefineDependenciesArgs) { + availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembleSet = workbenchSession.getEnsembleSet(); + + const ensembleIdents = ensembleSet + .getRegularEnsembleArray() + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + const observedSurfaceMetadataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + + if (!ensembleIdent) { + return null; + } + + return await queryClient.fetchQuery({ + ...getObservedSurfacesMetadataOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + }, + signal: abortSignal, + }), + }); + }); + + availableSettingsUpdater(SettingType.SURFACE_ATTRIBUTE, ({ getHelperDependency }) => { + const data = getHelperDependency(observedSurfaceMetadataDep); + + if (!data) { + return []; + } + + const availableAttributes = [ + ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), + ]; + + return availableAttributes; + }); + + availableSettingsUpdater(SettingType.SURFACE_NAME, ({ getHelperDependency, getLocalSetting }) => { + const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const data = getHelperDependency(observedSurfaceMetadataDep); + + if (!attribute || !data) { + return []; + } + + const availableSurfaceNames = [ + ...Array.from( + new Set( + data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name) + ) + ), + ]; + + return availableSurfaceNames; + }); + + availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); + const data = getHelperDependency(observedSurfaceMetadataDep); + + if (!attribute || !surfaceName || !data) { + return []; + } + + const availableTimeOrIntervals: string[] = []; + const availableTimeTypes = [ + ...Array.from( + new Set( + data.surfaces + .filter((surface) => surface.attribute_name === attribute && surface.name === surfaceName) + .map((el) => el.time_type) + ) + ), + ]; + + if (availableTimeTypes.includes(SurfaceTimeType_api.NO_TIME)) { + availableTimeOrIntervals.push(SurfaceTimeType_api.NO_TIME); + } + if (availableTimeTypes.includes(SurfaceTimeType_api.TIME_POINT)) { + availableTimeOrIntervals.push(...data.time_points_iso_str); + } + if (availableTimeTypes.includes(SurfaceTimeType_api.INTERVAL)) { + availableTimeOrIntervals.push(...data.time_intervals_iso_str); + } + + return availableTimeOrIntervals; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/index.ts new file mode 100644 index 000000000..5309f6604 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/index.ts @@ -0,0 +1,2 @@ +export { ObservedSurfaceLayer } from "./ObservedSurfaceLayer"; +export { ObservedSurfaceSettingsContext } from "./ObservedSurfaceSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts new file mode 100644 index 000000000..37abb48dc --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts @@ -0,0 +1,9 @@ +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +export type ObservedSurfaceSettings = { + [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; + [SettingType.SURFACE_ATTRIBUTE]: string | null; + [SettingType.SURFACE_NAME]: string | null; + [SettingType.TIME_OR_INTERVAL]: string | null; +}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts new file mode 100644 index 000000000..1020308d7 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -0,0 +1,198 @@ +import { getGridParameterOptions, getGridSurfaceOptions } from "@api"; +import { + GridMappedProperty_trans, + GridSurface_trans, + transformGridMappedProperty, + transformGridSurface, +} from "@modules/3DViewer/view/queries/queryDataTransforms"; +import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; +import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; +import { QueryClient } from "@tanstack/react-query"; + +import { isEqual } from "lodash"; + +import { RealizationGridSettingsContext } from "./RealizationGridSettingsContext"; +import { RealizationGridSettings } from "./types"; + +export class RealizationGridLayer + implements + Layer< + RealizationGridSettings, + { + gridSurfaceData: GridSurface_trans; + gridParameterData: GridMappedProperty_trans; + } + > +{ + private _layerDelegate: LayerDelegate< + RealizationGridSettings, + { + gridSurfaceData: GridSurface_trans; + gridParameterData: GridMappedProperty_trans; + } + >; + private _itemDelegate: ItemDelegate; + + constructor(layerManager: LayerManager) { + this._itemDelegate = new ItemDelegate("Realization Grid layer", layerManager); + this._layerDelegate = new LayerDelegate( + this, + layerManager, + new RealizationGridSettingsContext(layerManager), + LayerColoringType.COLORSCALE + ); + } + + getSettingsContext() { + return this._layerDelegate.getSettingsContext(); + } + + getItemDelegate(): ItemDelegate { + return this._itemDelegate; + } + + getLayerDelegate(): LayerDelegate< + RealizationGridSettings, + { + gridSurfaceData: GridSurface_trans; + gridParameterData: GridMappedProperty_trans; + } + > { + return this._layerDelegate; + } + + doSettingsChangesRequireDataRefetch( + prevSettings: RealizationGridSettings, + newSettings: RealizationGridSettings + ): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeBoundingBox(): BoundingBox | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return { + x: [ + data.gridSurfaceData.origin_utm_x + data.gridSurfaceData.xmin, + data.gridSurfaceData.origin_utm_x + data.gridSurfaceData.xmax, + ], + y: [ + data.gridSurfaceData.origin_utm_y + data.gridSurfaceData.ymin, + data.gridSurfaceData.origin_utm_y + data.gridSurfaceData.ymax, + ], + z: [data.gridSurfaceData.zmin, data.gridSurfaceData.zmax], + }; + } + + makeValueRange(): [number, number] | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return [data.gridParameterData.min_grid_prop_value, data.gridParameterData.max_grid_prop_value]; + } + + fetchData(queryClient: QueryClient): Promise<{ + gridSurfaceData: GridSurface_trans; + gridParameterData: GridMappedProperty_trans; + }> { + const settings = this.getSettingsContext().getDelegate().getSettings(); + const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); + const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); + const gridName = settings[SettingType.GRID_NAME].getDelegate().getValue(); + const attribute = settings[SettingType.GRID_ATTRIBUTE].getDelegate().getValue(); + let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + if (timeOrInterval === "NO_TIME") { + timeOrInterval = null; + } + let availableDimensions = settings[SettingType.GRID_LAYER].getDelegate().getAvailableValues(); + if (!availableDimensions.length || availableDimensions[0] === null) { + availableDimensions = [0, 0, 0]; + } + const layerIndex = settings[SettingType.GRID_LAYER].getDelegate().getValue(); + const iMin = 0; + const iMax = availableDimensions[0] || 0; + const jMin = 0; + const jMax = availableDimensions[1] || 0; + const kMin = layerIndex || 0; + const kMax = layerIndex || 0; + const queryKey = [ + "gridParameter", + ensembleIdent, + gridName, + attribute, + timeOrInterval, + realizationNum, + iMin, + iMax, + jMin, + jMax, + kMin, + kMax, + ]; + this._layerDelegate.registerQueryKey(queryKey); + + const gridParameterPromise = queryClient + .fetchQuery({ + ...getGridParameterOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + grid_name: gridName ?? "", + parameter_name: attribute ?? "", + parameter_time_or_interval_str: timeOrInterval, + realization_num: realizationNum ?? 0, + i_min: iMin, + i_max: iMax - 1, + j_min: jMin, + j_max: jMax - 1, + k_min: kMin, + k_max: kMax, + }, + }), + }) + .then(transformGridMappedProperty); + + const gridSurfacePromise = queryClient + .fetchQuery({ + ...getGridSurfaceOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + grid_name: gridName ?? "", + realization_num: realizationNum ?? 0, + i_min: iMin, + i_max: iMax - 1, + j_min: jMin, + j_max: jMax - 1, + k_min: kMin, + k_max: kMax, + }, + }), + }) + .then(transformGridSurface); + + return Promise.all([gridSurfacePromise, gridParameterPromise]).then(([gridSurfaceData, gridParameterData]) => ({ + gridSurfaceData, + gridParameterData, + })); + } + + serializeState(): SerializedLayer { + return this._layerDelegate.serializeState(); + } + + deserializeState(serializedState: SerializedLayer): void { + this._layerDelegate.deserializeState(serializedState); + } +} + +LayerRegistry.registerLayer(RealizationGridLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts new file mode 100644 index 000000000..88bb973b6 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts @@ -0,0 +1,176 @@ +import { getGridModelsInfoOptions } from "@api"; +import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; +import { GridAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting"; +import { GridLayerSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerSetting"; +import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; +import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; +import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; +import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +import { RealizationGridSettings } from "./types"; + +export class RealizationGridSettingsContext implements SettingsContext { + private _contextDelegate: SettingsContextDelegate; + + constructor(layerManager: LayerManager) { + this._contextDelegate = new SettingsContextDelegate( + this, + layerManager, + { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.REALIZATION]: new RealizationSetting(), + [SettingType.GRID_NAME]: new GridNameSetting(), + [SettingType.GRID_ATTRIBUTE]: new GridAttributeSetting(), + [SettingType.GRID_LAYER]: new GridLayerSetting(), + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + [SettingType.SHOW_GRID_LINES]: new ShowGridLinesSetting(), + } + ); + } + + areCurrentSettingsValid(settings: RealizationGridSettings): boolean { + return ( + settings[SettingType.ENSEMBLE] !== null && + settings[SettingType.REALIZATION] !== null && + settings[SettingType.GRID_NAME] !== null && + settings[SettingType.GRID_ATTRIBUTE] !== null && + settings[SettingType.GRID_LAYER] !== null && + settings[SettingType.TIME_OR_INTERVAL] !== null + ); + } + + getDelegate(): SettingsContextDelegate { + return this._contextDelegate; + } + + getSettings() { + return this._contextDelegate.getSettings(); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + queryClient, + }: DefineDependenciesArgs) { + availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); + + if (!ensembleIdent) { + return []; + } + + const realizations = realizationFilterFunc(ensembleIdent); + + return [...realizations]; + }); + const realizationGridDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realization = getLocalSetting(SettingType.REALIZATION); + + if (!ensembleIdent || realization === null) { + return null; + } + + return await queryClient.fetchQuery({ + ...getGridModelsInfoOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + realization_num: realization, + }, + signal: abortSignal, + }), + }); + }); + + availableSettingsUpdater(SettingType.GRID_NAME, ({ getHelperDependency }) => { + const data = getHelperDependency(realizationGridDataDep); + + if (!data) { + return []; + } + + const availableGridNames = [...Array.from(new Set(data.map((gridModelInfo) => gridModelInfo.grid_name)))]; + + return availableGridNames; + }); + + availableSettingsUpdater(SettingType.GRID_ATTRIBUTE, ({ getLocalSetting, getHelperDependency }) => { + const gridName = getLocalSetting(SettingType.GRID_NAME); + const data = getHelperDependency(realizationGridDataDep); + + if (!gridName || !data) { + return []; + } + + const gridAttributeArr = + data.find((gridModel) => gridModel.grid_name === gridName)?.property_info_arr ?? []; + + const availableGridAttributes = [ + ...Array.from(new Set(gridAttributeArr.map((gridAttribute) => gridAttribute.property_name))), + ]; + + return availableGridAttributes; + }); + + availableSettingsUpdater(SettingType.GRID_LAYER, ({ getLocalSetting, getHelperDependency }) => { + const gridName = getLocalSetting(SettingType.GRID_NAME); + const data = getHelperDependency(realizationGridDataDep); + + if (!gridName || !data) { + return []; + } + + const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; + const availableGridLayers: number[] = []; + if (gridDimensions) { + availableGridLayers.push(gridDimensions.i_count); + availableGridLayers.push(gridDimensions.j_count); + availableGridLayers.push(gridDimensions.k_count); + } + + return availableGridLayers; + }); + + availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const gridName = getLocalSetting(SettingType.GRID_NAME); + const gridAttribute = getLocalSetting(SettingType.GRID_ATTRIBUTE); + const data = getHelperDependency(realizationGridDataDep); + + if (!gridName || !gridAttribute || !data) { + return []; + } + + const gridAttributeArr = + data.find((gridModel) => gridModel.grid_name === gridName)?.property_info_arr ?? []; + + const availableTimeOrIntervals = [ + ...Array.from( + new Set( + gridAttributeArr + .filter((attr) => attr.property_name === gridAttribute) + .map((gridAttribute) => gridAttribute.iso_date_or_interval ?? "NO_TIME") + ) + ), + ]; + + return availableTimeOrIntervals; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/index.ts new file mode 100644 index 000000000..377b23ffe --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/index.ts @@ -0,0 +1,2 @@ +export { RealizationGridLayer } from "./RealizationGridLayer"; +export { RealizationGridSettingsContext } from "./RealizationGridSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts new file mode 100644 index 000000000..f5186f280 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts @@ -0,0 +1,12 @@ +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +export type RealizationGridSettings = { + [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; + [SettingType.REALIZATION]: number | null; + [SettingType.GRID_ATTRIBUTE]: string | null; + [SettingType.GRID_NAME]: string | null; + [SettingType.GRID_LAYER]: number | null; + [SettingType.TIME_OR_INTERVAL]: string | null; + [SettingType.SHOW_GRID_LINES]: boolean; +}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts new file mode 100644 index 000000000..ed3d45891 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts @@ -0,0 +1,121 @@ +import { PolygonData_api, getPolygonsDataOptions } from "@api"; +import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; +import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; +import { QueryClient } from "@tanstack/react-query"; + +import { isEqual } from "lodash"; + +import { RealizationPolygonsSettingsContext } from "./RealizationPolygonsSettingsContext"; +import { RealizationPolygonsSettings } from "./types"; + +export class RealizationPolygonsLayer implements Layer { + private _layerDelegate: LayerDelegate; + private _itemDelegate: ItemDelegate; + + constructor(layerManager: LayerManager) { + this._itemDelegate = new ItemDelegate("Realization Polygons", layerManager); + this._layerDelegate = new LayerDelegate( + this, + layerManager, + new RealizationPolygonsSettingsContext(layerManager), + LayerColoringType.NONE + ); + } + + getSettingsContext() { + return this._layerDelegate.getSettingsContext(); + } + + getItemDelegate(): ItemDelegate { + return this._itemDelegate; + } + + getLayerDelegate(): LayerDelegate { + return this._layerDelegate; + } + + doSettingsChangesRequireDataRefetch( + prevSettings: RealizationPolygonsSettings, + newSettings: RealizationPolygonsSettings + ): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeBoundingBox(): BoundingBox | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + const bbox: BoundingBox = { + x: [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY], + y: [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY], + z: [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY], + }; + + for (const polygon of data) { + for (const point of polygon.x_arr) { + bbox.x[0] = Math.min(bbox.x[0], point); + bbox.x[1] = Math.max(bbox.x[1], point); + } + for (const point of polygon.y_arr) { + bbox.y[0] = Math.min(bbox.y[0], point); + bbox.y[1] = Math.max(bbox.y[1], point); + } + for (const point of polygon.z_arr) { + bbox.z[0] = Math.min(bbox.z[0], point); + bbox.z[1] = Math.max(bbox.z[1], point); + } + } + + return bbox; + } + + fetchData(queryClient: QueryClient): Promise { + const settings = this.getSettingsContext().getDelegate().getSettings(); + const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); + const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); + const polygonsName = settings[SettingType.POLYGONS_NAME].getDelegate().getValue(); + const polygonsAttribute = settings[SettingType.POLYGONS_ATTRIBUTE].getDelegate().getValue(); + + const queryOptions = getPolygonsDataOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + realization_num: realizationNum ?? 0, + name: polygonsName ?? "", + attribute: polygonsAttribute ?? "", + }, + }); + + this._layerDelegate.registerQueryKey(queryOptions.queryKey); + + const promise = queryClient.fetchQuery({ + ...getPolygonsDataOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + realization_num: realizationNum ?? 0, + name: polygonsName ?? "", + attribute: polygonsAttribute ?? "", + }, + }), + }); + + return promise; + } + + serializeState(): SerializedLayer { + return this._layerDelegate.serializeState(); + } + + deserializeState(serializedState: SerializedLayer): void { + this._layerDelegate.deserializeState(serializedState); + } +} + +LayerRegistry.registerLayer(RealizationPolygonsLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts new file mode 100644 index 000000000..fdd1ce3c6 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts @@ -0,0 +1,116 @@ +import { getPolygonsDirectoryOptions } from "@api"; +import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; +import { PolygonsAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/PolygonsAttributeSetting"; +import { PolygonsNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/PolygonsNameSetting"; +import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +import { RealizationPolygonsSettings } from "./types"; + +export class RealizationPolygonsSettingsContext implements SettingsContext { + private _contextDelegate: SettingsContextDelegate; + + constructor(layerManager: LayerManager) { + this._contextDelegate = new SettingsContextDelegate< + RealizationPolygonsSettings, + keyof RealizationPolygonsSettings + >(this, layerManager, { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.REALIZATION]: new RealizationSetting(), + [SettingType.POLYGONS_ATTRIBUTE]: new PolygonsAttributeSetting(), + [SettingType.POLYGONS_NAME]: new PolygonsNameSetting(), + }); + } + + getDelegate(): SettingsContextDelegate { + return this._contextDelegate; + } + + getSettings() { + return this._contextDelegate.getSettings(); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + queryClient, + }: DefineDependenciesArgs) { + availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); + + if (!ensembleIdent) { + return []; + } + + const realizations = realizationFilterFunc(ensembleIdent); + + return [...realizations]; + }); + + const realizationPolygonsMetadataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + + if (!ensembleIdent) { + return null; + } + + return await queryClient.fetchQuery({ + ...getPolygonsDirectoryOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + }, + signal: abortSignal, + }), + }); + }); + + availableSettingsUpdater(SettingType.POLYGONS_ATTRIBUTE, ({ getHelperDependency }) => { + const data = getHelperDependency(realizationPolygonsMetadataDep); + + if (!data) { + return []; + } + + const availableAttributes = [ + ...Array.from(new Set(data.map((polygonsMeta) => polygonsMeta.attribute_name))), + ]; + + return availableAttributes; + }); + + availableSettingsUpdater(SettingType.POLYGONS_NAME, ({ getHelperDependency, getLocalSetting }) => { + const attribute = getLocalSetting(SettingType.POLYGONS_ATTRIBUTE); + const data = getHelperDependency(realizationPolygonsMetadataDep); + + if (!attribute || !data) { + return []; + } + + const availableSurfaceNames = [ + ...Array.from( + new Set( + data.filter((polygonsMeta) => polygonsMeta.attribute_name === attribute).map((el) => el.name) + ) + ), + ]; + + return availableSurfaceNames; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/index.ts new file mode 100644 index 000000000..6e24a10fa --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/index.ts @@ -0,0 +1,2 @@ +export { RealizationPolygonsLayer } from "./RealizationPolygonsLayer"; +export { RealizationPolygonsSettingsContext } from "./RealizationPolygonsSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/types.ts new file mode 100644 index 000000000..d18b2ac32 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/types.ts @@ -0,0 +1,9 @@ +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +export type RealizationPolygonsSettings = { + [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; + [SettingType.REALIZATION]: number | null; + [SettingType.POLYGONS_ATTRIBUTE]: string | null; + [SettingType.POLYGONS_NAME]: string | null; +}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts new file mode 100644 index 000000000..52be9cf8c --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts @@ -0,0 +1,130 @@ +import { SurfaceDataPng_api, SurfaceTimeType_api } from "@api"; +import { getSurfaceDataOptions } from "@api"; +import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; +import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; +import { FullSurfaceAddress, SurfaceAddressBuilder } from "@modules/_shared/Surface"; +import { SurfaceDataFloat_trans, transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; +import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; +import { QueryClient } from "@tanstack/react-query"; + +import { isEqual } from "lodash"; + +import { RealizationSurfaceSettingsContext } from "./RealizationSurfaceSettingsContext"; +import { RealizationSurfaceSettings } from "./types"; + +export class RealizationSurfaceLayer + implements Layer +{ + private _layerDelegate: LayerDelegate; + private _itemDelegate: ItemDelegate; + + constructor(layerManager: LayerManager) { + this._itemDelegate = new ItemDelegate("Realization Surface", layerManager); + this._layerDelegate = new LayerDelegate( + this, + layerManager, + new RealizationSurfaceSettingsContext(layerManager), + LayerColoringType.COLORSCALE + ); + } + + getSettingsContext() { + return this._layerDelegate.getSettingsContext(); + } + + getItemDelegate(): ItemDelegate { + return this._itemDelegate; + } + + getLayerDelegate(): LayerDelegate { + return this._layerDelegate; + } + + doSettingsChangesRequireDataRefetch( + prevSettings: RealizationSurfaceSettings, + newSettings: RealizationSurfaceSettings + ): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeBoundingBox(): BoundingBox | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return { + x: [data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.max_x], + y: [data.transformed_bbox_utm.min_y, data.transformed_bbox_utm.max_y], + z: [0, 0], + }; + } + + makeValueRange(): [number, number] | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return [data.value_min, data.value_max]; + } + + fetchData(queryClient: QueryClient): Promise { + let surfaceAddress: FullSurfaceAddress | null = null; + const addrBuilder = new SurfaceAddressBuilder(); + + const settings = this.getSettingsContext().getDelegate().getSettings(); + const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); + const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); + const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); + const attribute = settings[SettingType.SURFACE_ATTRIBUTE].getDelegate().getValue(); + const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + + if (ensembleIdent && surfaceName && attribute && realizationNum !== null) { + addrBuilder.withEnsembleIdent(ensembleIdent); + addrBuilder.withName(surfaceName); + addrBuilder.withAttribute(attribute); + addrBuilder.withRealization(realizationNum); + + if (timeOrInterval !== SurfaceTimeType_api.NO_TIME) { + addrBuilder.withTimeOrInterval(timeOrInterval); + } + + surfaceAddress = addrBuilder.buildRealizationAddress(); + } + + const surfAddrStr = surfaceAddress ? encodeSurfAddrStr(surfaceAddress) : null; + + const queryKey = ["getSurfaceData", surfAddrStr, null, "png"]; + + this._layerDelegate.registerQueryKey(queryKey); + + const promise = queryClient + .fetchQuery({ + ...getSurfaceDataOptions({ + query: { + surf_addr_str: surfAddrStr ?? "", + data_format: "png", + resample_to_def_str: null, + }, + }), + }) + .then((data) => transformSurfaceData(data)); + + return promise; + } + + serializeState(): SerializedLayer { + return this._layerDelegate.serializeState(); + } + + deserializeState(serializedState: SerializedLayer): void { + this._layerDelegate.deserializeState(serializedState); + } +} + +LayerRegistry.registerLayer(RealizationSurfaceLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts new file mode 100644 index 000000000..9829608da --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts @@ -0,0 +1,152 @@ +import { SurfaceTimeType_api } from "@api"; +import { getRealizationSurfacesMetadataOptions } from "@api"; +import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; +import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; +import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; +import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; +import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +import { RealizationSurfaceSettings } from "./types"; + +export class RealizationSurfaceSettingsContext implements SettingsContext { + private _contextDelegate: SettingsContextDelegate; + + constructor(layerManager: LayerManager) { + this._contextDelegate = new SettingsContextDelegate< + RealizationSurfaceSettings, + keyof RealizationSurfaceSettings + >(this, layerManager, { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.REALIZATION]: new RealizationSetting(), + [SettingType.SURFACE_ATTRIBUTE]: new SurfaceAttributeSetting(), + [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + }); + } + + getDelegate(): SettingsContextDelegate { + return this._contextDelegate; + } + + getSettings() { + return this._contextDelegate.getSettings(); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + queryClient, + }: DefineDependenciesArgs) { + availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); + + if (!ensembleIdent) { + return []; + } + + const realizations = realizationFilterFunc(ensembleIdent); + + return [...realizations]; + }); + + const realizationSurfaceMetadataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + + if (!ensembleIdent) { + return null; + } + + return await queryClient.fetchQuery({ + ...getRealizationSurfacesMetadataOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + }, + signal: abortSignal, + }), + }); + }); + + availableSettingsUpdater(SettingType.SURFACE_ATTRIBUTE, ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSurfaceMetadataDep); + + if (!data) { + return []; + } + + const availableAttributes = [ + ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), + ]; + + return availableAttributes; + }); + + availableSettingsUpdater(SettingType.SURFACE_NAME, ({ getHelperDependency, getLocalSetting }) => { + const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const data = getHelperDependency(realizationSurfaceMetadataDep); + + if (!attribute || !data) { + return []; + } + + const availableSurfaceNames = [ + ...Array.from( + new Set( + data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name) + ) + ), + ]; + + return availableSurfaceNames; + }); + + availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); + const data = getHelperDependency(realizationSurfaceMetadataDep); + + if (!attribute || !surfaceName || !data) { + return []; + } + + const availableTimeOrIntervals: string[] = []; + const availableTimeTypes = [ + ...Array.from( + new Set( + data.surfaces + .filter((surface) => surface.attribute_name === attribute && surface.name === surfaceName) + .map((el) => el.time_type) + ) + ), + ]; + + if (availableTimeTypes.includes(SurfaceTimeType_api.NO_TIME)) { + availableTimeOrIntervals.push(SurfaceTimeType_api.NO_TIME); + } + if (availableTimeTypes.includes(SurfaceTimeType_api.TIME_POINT)) { + availableTimeOrIntervals.push(...data.time_points_iso_str); + } + if (availableTimeTypes.includes(SurfaceTimeType_api.INTERVAL)) { + availableTimeOrIntervals.push(...data.time_intervals_iso_str); + } + + return availableTimeOrIntervals; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts new file mode 100644 index 000000000..ed6642a0d --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts @@ -0,0 +1,2 @@ +export { RealizationSurfaceLayer } from "./RealizationSurfaceLayer"; +export { RealizationSurfaceSettingsContext } from "./RealizationSurfaceSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts new file mode 100644 index 000000000..c0225d599 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts @@ -0,0 +1,10 @@ +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +export type RealizationSurfaceSettings = { + [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; + [SettingType.REALIZATION]: number | null; + [SettingType.SURFACE_ATTRIBUTE]: string | null; + [SettingType.SURFACE_NAME]: string | null; + [SettingType.TIME_OR_INTERVAL]: string | null; +}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts new file mode 100644 index 000000000..28541edef --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts @@ -0,0 +1,155 @@ +import { SurfaceDataPng_api, SurfaceTimeType_api, getSurfaceDataOptions } from "@api"; +import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; +import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; +import { FullSurfaceAddress, SurfaceAddressBuilder } from "@modules/_shared/Surface"; +import { SurfaceDataFloat_trans, transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; +import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; +import { QueryClient } from "@tanstack/react-query"; + +import { isEqual } from "lodash"; + +import { StatisticalSurfaceSettingsContext } from "./StatisticalSurfaceSettingsContext"; +import { StatisticalSurfaceSettings } from "./types"; + +export class StatisticalSurfaceLayer + implements Layer +{ + private _itemDelegate: ItemDelegate; + private _layerDelegate: LayerDelegate; + + constructor(layerManager: LayerManager) { + this._itemDelegate = new ItemDelegate("Statistical Surface", layerManager); + this._layerDelegate = new LayerDelegate( + this, + layerManager, + new StatisticalSurfaceSettingsContext(layerManager), + LayerColoringType.COLORSCALE + ); + } + + getSettingsContext() { + return this._layerDelegate.getSettingsContext(); + } + + getItemDelegate(): ItemDelegate { + return this._itemDelegate; + } + + getLayerDelegate(): LayerDelegate { + return this._layerDelegate; + } + + doSettingsChangesRequireDataRefetch( + prevSettings: StatisticalSurfaceSettings, + newSettings: StatisticalSurfaceSettings + ): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeBoundingBox(): BoundingBox | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return { + x: [data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.max_x], + y: [data.transformed_bbox_utm.min_y, data.transformed_bbox_utm.max_y], + z: [0, 0], + }; + } + + makeValueRange(): [number, number] | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return [data.value_min, data.value_max]; + } + + fetchData(queryClient: QueryClient): Promise { + let surfaceAddress: FullSurfaceAddress | null = null; + const addrBuilder = new SurfaceAddressBuilder(); + const workbenchSession = this.getLayerDelegate().getLayerManager().getWorkbenchSession(); + const settings = this.getSettingsContext().getDelegate().getSettings(); + const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); + const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); + const attribute = settings[SettingType.SURFACE_ATTRIBUTE].getDelegate().getValue(); + const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + const statisticFunction = settings[SettingType.STATISTIC_FUNCTION].getDelegate().getValue(); + const sensitivityNameCasePair = settings[SettingType.SENSITIVITY].getDelegate().getValue(); + + if (ensembleIdent && surfaceName && attribute) { + addrBuilder.withEnsembleIdent(ensembleIdent); + addrBuilder.withName(surfaceName); + addrBuilder.withAttribute(attribute); + + // Get filtered realizations from workbench + let filteredRealizations = workbenchSession + .getRealizationFilterSet() + .getRealizationFilterForEnsembleIdent(ensembleIdent) + .getFilteredRealizations(); + const currentEnsemble = workbenchSession.getEnsembleSet().findEnsemble(ensembleIdent); + + // If sensitivity is set, filter realizations further to only include the realizations that are in the sensitivity + if (sensitivityNameCasePair) { + const sensitivity = currentEnsemble + ?.getSensitivities() + ?.getCaseByName(sensitivityNameCasePair.sensitivityName, sensitivityNameCasePair.sensitivityCase); + + const sensitivityRealizations = sensitivity?.realizations ?? []; + + filteredRealizations = filteredRealizations.filter((realization) => + sensitivityRealizations.includes(realization) + ); + } + + // If realizations are filtered, update the address + const allRealizations = currentEnsemble?.getRealizations() ?? []; + if (!isEqual([...allRealizations], [...filteredRealizations])) { + addrBuilder.withStatisticRealizations([...filteredRealizations]); + } + + if (timeOrInterval !== SurfaceTimeType_api.NO_TIME) { + addrBuilder.withTimeOrInterval(timeOrInterval); + } + addrBuilder.withStatisticFunction(statisticFunction); + surfaceAddress = addrBuilder.buildStatisticalAddress(); + } + + const surfAddrStr = surfaceAddress ? encodeSurfAddrStr(surfaceAddress) : null; + + const queryOptions = getSurfaceDataOptions({ + query: { + surf_addr_str: surfAddrStr ?? "", + data_format: "png", + resample_to_def_str: null, + }, + }); + + this._layerDelegate.registerQueryKey(queryOptions.queryKey); + + const promise = queryClient + .fetchQuery({ + ...queryOptions, + }) + .then((data) => transformSurfaceData(data)); + + return promise; + } + + serializeState(): SerializedLayer { + return this._layerDelegate.serializeState(); + } + + deserializeState(serializedState: SerializedLayer): void { + this._layerDelegate.deserializeState(serializedState); + } +} + +LayerRegistry.registerLayer(StatisticalSurfaceLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts new file mode 100644 index 000000000..604ae0c28 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts @@ -0,0 +1,169 @@ +import { SurfaceStatisticFunction_api, SurfaceTimeType_api } from "@api"; +import { getRealizationSurfacesMetadataOptions } from "@api"; +import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; +import { + SensitivityNameCasePair, + SensitivitySetting, +} from "@modules/_shared/LayerFramework/settings/implementations/SensitivitySetting"; +import { StatisticFunctionSetting } from "@modules/_shared/LayerFramework/settings/implementations/StatisticFunctionSetting"; +import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; +import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; +import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +import { StatisticalSurfaceSettings } from "./types"; + +export class StatisticalSurfaceSettingsContext implements SettingsContext { + private _contextDelegate: SettingsContextDelegate; + + constructor(layerManager: LayerManager) { + this._contextDelegate = new SettingsContextDelegate< + StatisticalSurfaceSettings, + keyof StatisticalSurfaceSettings + >(this, layerManager, { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.STATISTIC_FUNCTION]: new StatisticFunctionSetting(), + [SettingType.SENSITIVITY]: new SensitivitySetting(), + [SettingType.SURFACE_ATTRIBUTE]: new SurfaceAttributeSetting(), + [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + }); + } + + getDelegate(): SettingsContextDelegate { + return this._contextDelegate; + } + + getSettings() { + return this._contextDelegate.getSettings(); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + workbenchSession, + queryClient, + }: DefineDependenciesArgs) { + availableSettingsUpdater(SettingType.STATISTIC_FUNCTION, () => Object.values(SurfaceStatisticFunction_api)); + availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + availableSettingsUpdater(SettingType.SENSITIVITY, ({ getLocalSetting }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + + if (!ensembleIdent) { + return []; + } + + const ensembleSet = workbenchSession.getEnsembleSet(); + const currentEnsemble = ensembleSet.findEnsemble(ensembleIdent); + const sensitivities = currentEnsemble?.getSensitivities()?.getSensitivityArr() ?? []; + if (sensitivities.length === 0) { + return []; + } + const availableSensitivityPairs: SensitivityNameCasePair[] = []; + sensitivities.map((sensitivity) => + sensitivity.cases.map((sensitivityCase) => { + availableSensitivityPairs.push({ + sensitivityName: sensitivity.name, + sensitivityCase: sensitivityCase.name, + }); + }) + ); + return availableSensitivityPairs; + }); + + const surfaceMetadataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + + if (!ensembleIdent) { + return null; + } + + return await queryClient.fetchQuery({ + ...getRealizationSurfacesMetadataOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + }, + signal: abortSignal, + }), + }); + }); + + availableSettingsUpdater(SettingType.SURFACE_ATTRIBUTE, ({ getHelperDependency }) => { + const data = getHelperDependency(surfaceMetadataDep); + + if (!data) { + return []; + } + + const availableAttributes = [ + ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), + ]; + + return availableAttributes; + }); + availableSettingsUpdater(SettingType.SURFACE_NAME, ({ getHelperDependency, getLocalSetting }) => { + const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const data = getHelperDependency(surfaceMetadataDep); + + if (!attribute || !data) { + return []; + } + + const availableSurfaceNames = [ + ...Array.from( + new Set( + data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name) + ) + ), + ]; + + return availableSurfaceNames; + }); + + availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); + const data = getHelperDependency(surfaceMetadataDep); + + if (!attribute || !surfaceName || !data) { + return []; + } + + const availableTimeOrIntervals: string[] = []; + const availableTimeTypes = [ + ...Array.from( + new Set( + data.surfaces + .filter((surface) => surface.attribute_name === attribute && surface.name === surfaceName) + .map((el) => el.time_type) + ) + ), + ]; + + if (availableTimeTypes.includes(SurfaceTimeType_api.NO_TIME)) { + availableTimeOrIntervals.push(SurfaceTimeType_api.NO_TIME); + } + if (availableTimeTypes.includes(SurfaceTimeType_api.TIME_POINT)) { + availableTimeOrIntervals.push(...data.time_points_iso_str); + } + if (availableTimeTypes.includes(SurfaceTimeType_api.INTERVAL)) { + availableTimeOrIntervals.push(...data.time_intervals_iso_str); + } + + return availableTimeOrIntervals; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/index.ts new file mode 100644 index 000000000..cf8f62a77 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/index.ts @@ -0,0 +1,2 @@ +export { StatisticalSurfaceLayer } from "./StatisticalSurfaceLayer"; +export { StatisticalSurfaceSettingsContext } from "./StatisticalSurfaceSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts new file mode 100644 index 000000000..9df5760de --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts @@ -0,0 +1,13 @@ +import { SurfaceStatisticFunction_api } from "@api"; +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { SensitivityNameCasePair } from "@modules/_shared/LayerFramework/settings/implementations/SensitivitySetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +export type StatisticalSurfaceSettings = { + [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; + [SettingType.STATISTIC_FUNCTION]: SurfaceStatisticFunction_api; + [SettingType.SENSITIVITY]: SensitivityNameCasePair | null; + [SettingType.SURFACE_ATTRIBUTE]: string | null; + [SettingType.SURFACE_NAME]: string | null; + [SettingType.TIME_OR_INTERVAL]: string | null; +}; diff --git a/frontend/src/modules/3DViewerNew/interfaces.ts b/frontend/src/modules/3DViewerNew/interfaces.ts new file mode 100644 index 000000000..914b5a703 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/interfaces.ts @@ -0,0 +1,24 @@ +import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; + +import { layerManagerAtom, preferredViewLayoutAtom } from "./settings/atoms/baseAtoms"; +import { PreferredViewLayout } from "./types"; + +import { LayerManager } from "../_shared/LayerFramework/framework/LayerManager/LayerManager"; + +export type SettingsToViewInterface = { + layerManager: LayerManager | null; + preferredViewLayout: PreferredViewLayout; +}; + +export type Interfaces = { + settingsToView: SettingsToViewInterface; +}; + +export const settingsToViewInterfaceInitialization: InterfaceInitialization = { + layerManager: (get) => { + return get(layerManagerAtom); + }, + preferredViewLayout: (get) => { + return get(preferredViewLayoutAtom); + }, +}; diff --git a/frontend/src/modules/3DViewerNew/loadModule.tsx b/frontend/src/modules/3DViewerNew/loadModule.tsx new file mode 100644 index 000000000..dfcfa27de --- /dev/null +++ b/frontend/src/modules/3DViewerNew/loadModule.tsx @@ -0,0 +1,13 @@ +import { ModuleRegistry } from "@framework/ModuleRegistry"; + +import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; +import { MODULE_NAME } from "./registerModule"; +import { Settings } from "./settings/settings"; +import { View } from "./view/view"; + +const module = ModuleRegistry.initModule(MODULE_NAME, { + settingsToViewInterfaceInitialization, +}); + +module.settingsFC = Settings; +module.viewFC = View; diff --git a/frontend/src/modules/3DViewerNew/preview.tsx b/frontend/src/modules/3DViewerNew/preview.tsx new file mode 100644 index 000000000..2a9746ab7 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/preview.tsx @@ -0,0 +1,8 @@ +import { DrawPreviewFunc } from "@framework/Preview"; +import previewImg from "./preview.webp"; + +export const preview: DrawPreviewFunc = function (width: number, height: number) { + return ( + + ); +}; diff --git a/frontend/src/modules/3DViewerNew/preview.webp b/frontend/src/modules/3DViewerNew/preview.webp new file mode 100644 index 0000000000000000000000000000000000000000..d180acb38d4be2486e7125d9adddd4b771a921cd GIT binary patch literal 52254 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 Td5qy!>Iqlnl?KhTd;kCd!im;n literal 0 HcmV?d00001 diff --git a/frontend/src/modules/3DViewerNew/registerModule.ts b/frontend/src/modules/3DViewerNew/registerModule.ts new file mode 100644 index 000000000..cb06a94f5 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/registerModule.ts @@ -0,0 +1,29 @@ +import { ModuleCategory, ModuleDevState } from "@framework/Module"; +import { ModuleDataTagId } from "@framework/ModuleDataTags"; +import { ModuleRegistry } from "@framework/ModuleRegistry"; + +import { Interfaces } from "./interfaces"; +import { preview } from "./preview"; + +export const MODULE_NAME: string = "3DViewerNew"; + +const description = "Generic 3D viewer for grid, surfaces, and wells."; + +ModuleRegistry.registerModule({ + moduleName: MODULE_NAME, + category: ModuleCategory.MAIN, + devState: ModuleDevState.DEV, + defaultTitle: "3D Viewer (new)", + preview, + description, + dataTagIds: [ + ModuleDataTagId.SURFACE, + ModuleDataTagId.DRILLED_WELLS, + ModuleDataTagId.SEISMIC, + ModuleDataTagId.GRID3D, + ModuleDataTagId.POLYGONS, + ], + onInstanceUnload: (instanceId) => { + window.localStorage.removeItem(`${instanceId}-settings`); + }, +}); diff --git a/frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts b/frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts new file mode 100644 index 000000000..2ebacfc7e --- /dev/null +++ b/frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts @@ -0,0 +1,8 @@ +import { PreferredViewLayout } from "@modules/2DViewer/types"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; + +import { atom } from "jotai"; + +export const userSelectedFieldIdentifierAtom = atom(null); +export const layerManagerAtom = atom(null); +export const preferredViewLayoutAtom = atom(PreferredViewLayout.VERTICAL); diff --git a/frontend/src/modules/3DViewerNew/settings/atoms/derivedAtoms.ts b/frontend/src/modules/3DViewerNew/settings/atoms/derivedAtoms.ts new file mode 100644 index 000000000..ae64f8ba7 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/settings/atoms/derivedAtoms.ts @@ -0,0 +1,19 @@ +import { EnsembleSetAtom } from "@framework/GlobalAtoms"; + +import { atom } from "jotai"; + +import { userSelectedFieldIdentifierAtom } from "./baseAtoms"; + +export const selectedFieldIdentifierAtom = atom((get) => { + const ensembleSet = get(EnsembleSetAtom); + const userSelectedField = get(userSelectedFieldIdentifierAtom); + + if ( + !userSelectedField || + !ensembleSet.getRegularEnsembleArray().some((ens) => ens.getFieldIdentifier() === userSelectedField) + ) { + return ensembleSet.getRegularEnsembleArray().at(0)?.getFieldIdentifier() ?? null; + } + + return userSelectedField; +}); diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx new file mode 100644 index 000000000..2a2935c1a --- /dev/null +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -0,0 +1,334 @@ +import React from "react"; + +import { Icon } from "@equinor/eds-core-react"; +import { color_palette, fault, grid_layer, settings, surface_layer, wellbore } from "@equinor/eds-icons"; +import { WorkbenchSession } from "@framework/WorkbenchSession"; +import { WorkbenchSettings } from "@framework/WorkbenchSettings"; +import { Menu } from "@lib/components/Menu"; +import { MenuButton } from "@lib/components/MenuButton"; +import { MenuHeading } from "@lib/components/MenuHeading"; +import { MenuItem } from "@lib/components/MenuItem"; +import { ObservedSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer"; +import { RealizationGridLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer"; +import { RealizationPolygonsLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer"; +import { RealizationSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; +import { StatisticalSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; +import { PreferredViewLayout } from "@modules/2DViewer/types"; +import { EnsembleSetting } from "@modules//_shared/LayerFramework/settings/implementations/EnsembleSetting"; +import { LayersActionGroup } from "@modules/_shared/LayerFramework/LayersActions"; +import { GroupDelegate, GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; +import { usePublishSubscribeTopicValue } from "@modules/_shared/LayerFramework/delegates/PublishSubscribeDelegate"; +import { ColorScale } from "@modules/_shared/LayerFramework/framework/ColorScale/ColorScale"; +import { DeltaSurface } from "@modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurface"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { LayerManagerComponent } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManagerComponent"; +import { SettingsGroup } from "@modules/_shared/LayerFramework/framework/SettingsGroup/SettingsGroup"; +import { SharedSetting } from "@modules/_shared/LayerFramework/framework/SharedSetting/SharedSetting"; +import { View } from "@modules/_shared/LayerFramework/framework/View/View"; +import { Group, Item, instanceofGroup, instanceofLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; +import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; +import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; +import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; +import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; +import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { Dropdown } from "@mui/base"; +import { + Check, + Panorama, + SettingsApplications, + Settings as SettingsIcon, + TableRowsOutlined, + ViewColumnOutlined, +} from "@mui/icons-material"; + +import { useAtom } from "jotai"; + +import { preferredViewLayoutAtom } from "../atoms/baseAtoms"; + +export type LayerManagerComponentWrapperProps = { + layerManager: LayerManager; + workbenchSession: WorkbenchSession; + workbenchSettings: WorkbenchSettings; +}; + +export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapperProps): React.ReactNode { + const colorSet = props.workbenchSettings.useColorSet(); + const [preferredViewLayout, setPreferredViewLayout] = useAtom(preferredViewLayoutAtom); + + const groupDelegate = props.layerManager.getGroupDelegate(); + usePublishSubscribeTopicValue(groupDelegate, GroupDelegateTopic.CHILDREN); + + function handleLayerAction(identifier: string, groupDelegate: GroupDelegate) { + const numSharedSettings = groupDelegate.findChildren((item) => { + return item instanceof SharedSetting; + }).length; + + const numViews = groupDelegate.getDescendantItems((item) => item instanceof View).length; + + switch (identifier) { + case "view": + groupDelegate.appendChild( + new View(numViews > 0 ? `View (${numViews})` : "View", props.layerManager, colorSet.getNextColor()) + ); + return; + case "delta-surface": + groupDelegate.insertChild(new DeltaSurface("Delta surface", props.layerManager), numSharedSettings); + return; + case "settings-group": + groupDelegate.insertChild(new SettingsGroup("Settings group", props.layerManager), numSharedSettings); + return; + case "color-scale": + groupDelegate.prependChild(new ColorScale("Color scale", props.layerManager)); + return; + case "observed-surface": + groupDelegate.insertChild(new ObservedSurfaceLayer(props.layerManager), numSharedSettings); + return; + case "statistical-surface": + groupDelegate.insertChild(new StatisticalSurfaceLayer(props.layerManager), numSharedSettings); + return; + case "realization-surface": + groupDelegate.insertChild(new RealizationSurfaceLayer(props.layerManager), numSharedSettings); + return; + case "realization-polygons": + groupDelegate.insertChild(new RealizationPolygonsLayer(props.layerManager), numSharedSettings); + return; + case "drilled-wellbore-trajectories": + groupDelegate.insertChild(new DrilledWellTrajectoriesLayer(props.layerManager), numSharedSettings); + return; + case "drilled-wellbore-picks": + groupDelegate.insertChild(new DrilledWellborePicksLayer(props.layerManager), numSharedSettings); + return; + case "realization-grid": + groupDelegate.insertChild(new RealizationGridLayer(props.layerManager), numSharedSettings); + return; + case "ensemble": + groupDelegate.prependChild(new SharedSetting(new EnsembleSetting(), props.layerManager)); + return; + case "realization": + groupDelegate.prependChild(new SharedSetting(new RealizationSetting(), props.layerManager)); + return; + case "surface-name": + groupDelegate.prependChild(new SharedSetting(new SurfaceNameSetting(), props.layerManager)); + return; + case "surface-attribute": + groupDelegate.prependChild(new SharedSetting(new SurfaceAttributeSetting(), props.layerManager)); + return; + case "Date": + groupDelegate.prependChild(new SharedSetting(new TimeOrIntervalSetting(), props.layerManager)); + return; + } + } + + function checkIfItemMoveAllowed(movedItem: Item, destinationItem: Group): boolean { + if (destinationItem instanceof DeltaSurface) { + if ( + instanceofLayer(movedItem) && + !( + movedItem instanceof RealizationSurfaceLayer || + movedItem instanceof StatisticalSurfaceLayer || + movedItem instanceof ObservedSurfaceLayer + ) + ) { + return false; + } + + if (instanceofGroup(movedItem)) { + return false; + } + + if (destinationItem.getGroupDelegate().findChildren((item) => instanceofLayer(item)).length >= 2) { + return false; + } + } + + return true; + } + + const hasView = groupDelegate.getDescendantItems((item) => item instanceof View).length > 0; + const adjustedLayerActions = hasView ? LAYER_ACTIONS : INITIAL_LAYER_ACTIONS; + + return ( + + + + +

+ Preferred view layout + setPreferredViewLayout(PreferredViewLayout.HORIZONTAL)} + > + Horizontal + + setPreferredViewLayout(PreferredViewLayout.VERTICAL)} + > + Vertical + + + + } + layerActions={adjustedLayerActions} + onLayerAction={handleLayerAction} + isMoveAllowed={checkIfItemMoveAllowed} + /> + ); +} + +type ViewLayoutMenuItemProps = { + checked: boolean; + onClick: () => void; + children: React.ReactNode; +}; + +function ViewLayoutMenuItem(props: ViewLayoutMenuItemProps): React.ReactNode { + return ( + +
+
{props.checked && }
+
{props.children}
+
+
+ ); +} + +const INITIAL_LAYER_ACTIONS: LayersActionGroup[] = [ + { + label: "Groups", + children: [ + { + identifier: "view", + icon: , + label: "View", + }, + { + identifier: "settings-group", + icon: , + label: "Settings group", + }, + ], + }, +]; + +const LAYER_ACTIONS: LayersActionGroup[] = [ + { + label: "Groups", + children: [ + { + identifier: "view", + icon: , + label: "View", + }, + { + identifier: "settings-group", + icon: , + label: "Settings group", + }, + ], + }, + { + label: "Layers", + children: [ + { + label: "Surfaces", + children: [ + { + identifier: "observed-surface", + icon: , + label: "Observed Surface", + }, + { + identifier: "statistical-surface", + icon: , + label: "Statistical Surface", + }, + { + identifier: "realization-surface", + icon: , + label: "Realization Surface", + }, + ], + }, + { + label: "Wells", + children: [ + { + identifier: "drilled-wellbore-trajectories", + icon: , + label: "Drilled Wellbore Trajectories", + }, + { + identifier: "drilled-wellbore-picks", + icon: , + label: "Drilled Wellbore Picks", + }, + ], + }, + { + label: "Polygons", + children: [ + { + identifier: "realization-polygons", + icon: , + label: "Realization Polygons", + }, + ], + }, + { + label: "Others", + children: [ + { + identifier: "realization-grid", + icon: , + label: "Realization Grid", + }, + ], + }, + ], + }, + { + label: "Shared Settings", + children: [ + { + identifier: "ensemble", + icon: , + label: "Ensemble", + }, + { + identifier: "realization", + icon: , + label: "Realization", + }, + { + identifier: "surface-name", + icon: , + label: "Surface Name", + }, + { + identifier: "surface-attribute", + icon: , + label: "Surface Attribute", + }, + { + identifier: "Date", + icon: , + label: "Date", + }, + ], + }, + { + label: "Utilities", + children: [ + { + identifier: "color-scale", + icon: , + label: "Color scale", + }, + ], + }, +]; diff --git a/frontend/src/modules/3DViewerNew/settings/settings.tsx b/frontend/src/modules/3DViewerNew/settings/settings.tsx new file mode 100644 index 000000000..aad2cc170 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/settings/settings.tsx @@ -0,0 +1,146 @@ +import React from "react"; + +import { ModuleSettingsProps } from "@framework/Module"; +import { useEnsembleSet } from "@framework/WorkbenchSession"; +import { FieldDropdown } from "@framework/components/FieldDropdown"; +import { CollapsibleGroup } from "@lib/components/CollapsibleGroup"; +import { GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; +import { useQueryClient } from "@tanstack/react-query"; + +import { useAtom, useAtomValue, useSetAtom } from "jotai"; + +import { layerManagerAtom, preferredViewLayoutAtom, userSelectedFieldIdentifierAtom } from "./atoms/baseAtoms"; +import { selectedFieldIdentifierAtom } from "./atoms/derivedAtoms"; +import { LayerManagerComponentWrapper } from "./components/layerManagerComponentWrapper"; + +import { LayerManager, LayerManagerTopic } from "../../_shared/LayerFramework/framework/LayerManager/LayerManager"; + +export function Settings(props: ModuleSettingsProps): React.ReactNode { + const ensembleSet = useEnsembleSet(props.workbenchSession); + const queryClient = useQueryClient(); + + const [layerManager, setLayerManager] = useAtom(layerManagerAtom); + + const fieldIdentifier = useAtomValue(selectedFieldIdentifierAtom); + const setFieldIdentifier = useSetAtom(userSelectedFieldIdentifierAtom); + const [preferredViewLayout, setPreferredViewLayout] = useAtom(preferredViewLayoutAtom); + + const persistState = React.useCallback( + function persistLayerManagerState() { + if (!layerManager) { + return; + } + + const serializedState = { + layerManager: layerManager.serializeState(), + fieldIdentifier, + preferredViewLayout, + }; + window.localStorage.setItem( + `${props.settingsContext.getInstanceIdString()}-settings`, + JSON.stringify(serializedState) + ); + }, + [layerManager, fieldIdentifier, preferredViewLayout, props.settingsContext] + ); + + const applyPersistedState = React.useCallback( + function applyPersistedState(layerManager: LayerManager) { + const serializedState = window.localStorage.getItem( + `${props.settingsContext.getInstanceIdString()}-settings` + ); + if (!serializedState) { + return; + } + + const parsedState = JSON.parse(serializedState); + if (parsedState.fieldIdentifier) { + setFieldIdentifier(parsedState.fieldIdentifier); + } + if (parsedState.preferredViewLayout) { + setPreferredViewLayout(parsedState.preferredViewLayout); + } + + if (parsedState.layerManager) { + if (!layerManager) { + return; + } + layerManager.deserializeState(parsedState.layerManager); + } + }, + [setFieldIdentifier, setPreferredViewLayout, props.settingsContext] + ); + + React.useEffect( + function onMountEffect() { + const newLayerManager = new LayerManager(props.workbenchSession, props.workbenchSettings, queryClient); + setLayerManager(newLayerManager); + + applyPersistedState(newLayerManager); + + return function onUnmountEffect() { + newLayerManager.beforeDestroy(); + }; + }, + [setLayerManager, props.workbenchSession, props.workbenchSettings, queryClient, applyPersistedState] + ); + + React.useEffect( + function onLayerManagerChangeEffect() { + if (!layerManager) { + return; + } + + persistState(); + + const unsubscribeDataRev = layerManager + .getPublishSubscribeDelegate() + .makeSubscriberFunction(LayerManagerTopic.LAYER_DATA_REVISION)(persistState); + + const unsubscribeExpands = layerManager + .getGroupDelegate() + .getPublishSubscribeDelegate() + .makeSubscriberFunction(GroupDelegateTopic.CHILDREN_EXPANSION_STATES)(persistState); + + return function onUnmountEffect() { + layerManager.beforeDestroy(); + unsubscribeDataRev(); + unsubscribeExpands(); + }; + }, + [layerManager, props.workbenchSession, props.workbenchSettings, persistState] + ); + + React.useEffect( + function onFieldIdentifierChangedEffect() { + if (!layerManager) { + return; + } + layerManager.updateGlobalSetting("fieldId", fieldIdentifier); + }, + [fieldIdentifier, layerManager] + ); + + function handleFieldChange(fieldId: string | null) { + setFieldIdentifier(fieldId); + if (!layerManager) { + return; + } + layerManager.updateGlobalSetting("fieldId", fieldId); + } + + return ( +
+ + + + {layerManager && ( + + )} +
+ ); +} diff --git a/frontend/src/modules/3DViewerNew/types.ts b/frontend/src/modules/3DViewerNew/types.ts new file mode 100644 index 000000000..a457c6616 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/types.ts @@ -0,0 +1,4 @@ +export enum PreferredViewLayout { + HORIZONTAL = "horizontal", + VERTICAL = "vertical", +} diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx new file mode 100644 index 000000000..5c14031ca --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -0,0 +1,154 @@ +import React from "react"; + +import { View as DeckGlView } from "@deck.gl/core"; +import { ViewContext } from "@framework/ModuleContext"; +import { useViewStatusWriter } from "@framework/StatusWriter"; +import { PendingWrapper } from "@lib/components/PendingWrapper"; +import { useElementSize } from "@lib/hooks/useElementSize"; +import { Rect2D, outerRectContainsInnerRect } from "@lib/utils/geometry"; +import { Interfaces } from "@modules/2DViewer/interfaces"; +import { PreferredViewLayout } from "@modules/2DViewer/types"; +import { usePublishSubscribeTopicValue } from "@modules/_shared/LayerFramework/delegates/PublishSubscribeDelegate"; +import { LayerManager, LayerManagerTopic } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { BoundingBox } from "@modules/_shared/LayerFramework/interfaces"; +import { ColorLegendsContainer } from "@modules/_shared/components/ColorLegendsContainer"; +import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; +import { BoundingBox2D, ViewportType } from "@webviz/subsurface-viewer"; +import { ViewsType } from "@webviz/subsurface-viewer/dist/SubsurfaceViewer"; + +import { ReadoutWrapper } from "./ReadoutWrapper"; + +import { PlaceholderLayer } from "../customDeckGlLayers/PlaceholderLayer"; +import { DeckGlLayerWithPosition, recursivelyMakeViewsAndLayers } from "../utils/makeViewsAndLayers"; + +export type LayersWrapperProps = { + layerManager: LayerManager; + preferredViewLayout: PreferredViewLayout; + viewContext: ViewContext; +}; + +export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { + const [prevBoundingBox, setPrevBoundingBox] = React.useState(null); + + const mainDivRef = React.useRef(null); + const mainDivSize = useElementSize(mainDivRef); + const statusWriter = useViewStatusWriter(props.viewContext); + + usePublishSubscribeTopicValue(props.layerManager, LayerManagerTopic.LAYER_DATA_REVISION); + + const viewports: ViewportType[] = []; + const viewerLayers: DeckGlLayerWithPosition[] = []; + const viewportAnnotations: React.ReactNode[] = []; + const globalColorScales: ColorScaleWithId[] = []; + + const views: ViewsType = { + layout: [1, 1], + viewports: viewports, + showLabel: false, + }; + + let numCols = 0; + let numRows = 0; + + let numLoadingLayers = 0; + + const viewsAndLayers = recursivelyMakeViewsAndLayers(props.layerManager.getGroupDelegate()); + + numCols = Math.ceil(Math.sqrt(viewsAndLayers.views.length)); + numRows = Math.ceil(viewsAndLayers.views.length / numCols); + + if (props.preferredViewLayout === PreferredViewLayout.HORIZONTAL) { + [numCols, numRows] = [numRows, numCols]; + } + + views.layout = [numCols, numRows]; + + viewerLayers.push(...viewsAndLayers.layers); + globalColorScales.push(...viewsAndLayers.colorScales); + const globalLayerIds = viewsAndLayers.layers.map((layer) => layer.layer.id); + + for (const view of viewsAndLayers.views) { + viewports.push({ + id: view.id, + name: view.name, + isSync: true, + layerIds: [...globalLayerIds, ...view.layers.map((layer) => layer.layer.id), "placeholder"], + }); + viewerLayers.push(...view.layers); + + viewportAnnotations.push( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + /* @ts-expect-error */ + + +
+
+
+
{view.name}
+
+
+ + ); + } + + if (viewsAndLayers.boundingBox !== null) { + if (prevBoundingBox !== null) { + const oldBoundingRect: Rect2D | null = { + x: prevBoundingBox.x[0], + y: prevBoundingBox.y[0], + width: prevBoundingBox.x[1] - prevBoundingBox.x[0], + height: prevBoundingBox.y[1] - prevBoundingBox.y[0], + }; + + const newBoundingRect: Rect2D = { + x: viewsAndLayers.boundingBox.x[0], + y: viewsAndLayers.boundingBox.y[0], + width: viewsAndLayers.boundingBox.x[1] - viewsAndLayers.boundingBox.x[0], + height: viewsAndLayers.boundingBox.y[1] - viewsAndLayers.boundingBox.y[0], + }; + + if (!outerRectContainsInnerRect(oldBoundingRect, newBoundingRect)) { + setPrevBoundingBox(viewsAndLayers.boundingBox); + } + } else { + setPrevBoundingBox(viewsAndLayers.boundingBox); + } + } + + numLoadingLayers = viewsAndLayers.numLoadingLayers; + statusWriter.setLoading(viewsAndLayers.numLoadingLayers > 0); + + for (const message of viewsAndLayers.errorMessages) { + statusWriter.addError(message); + } + + let bounds: BoundingBox2D | undefined = undefined; + if (prevBoundingBox) { + bounds = [prevBoundingBox.x[0], prevBoundingBox.y[0], prevBoundingBox.x[1], prevBoundingBox.y[1]]; + } + + const layers = viewerLayers.toSorted((a, b) => b.position - a.position).map((layer) => layer.layer); + layers.push(new PlaceholderLayer({ id: "placeholder" })); + + return ( +
+ 0}> +
+ +
+
+
+ ); +} diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutBoxWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutBoxWrapper.tsx new file mode 100644 index 000000000..70a488c0b --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutBoxWrapper.tsx @@ -0,0 +1,117 @@ +import React from "react"; + +import { ReadoutBox, ReadoutItem } from "@modules/_shared/components/ReadoutBox"; +import { ExtendedLayerProps, LayerPickInfo } from "@webviz/subsurface-viewer"; + +import { isEqual } from "lodash"; + +// Needs extra distance for the left side; this avoids overlapping with legend elements +const READOUT_EDGE_DISTANCE_REM = { left: 6 }; + +function makePositionReadout(layerPickInfo: LayerPickInfo): ReadoutItem | null { + if (layerPickInfo.coordinate === undefined || layerPickInfo.coordinate.length < 2) { + return null; + } + return { + label: "Position", + info: [ + { + name: "x", + value: layerPickInfo.coordinate[0], + unit: "m", + }, + { + name: "y", + value: layerPickInfo.coordinate[1], + unit: "m", + }, + ], + }; +} + +export type ReadoutBoxWrapperProps = { + layerPickInfo: LayerPickInfo[]; + maxNumItems?: number; + visible?: boolean; +}; + +export function ReadoutBoxWrapper(props: ReadoutBoxWrapperProps): React.ReactNode { + const [infoData, setInfoData] = React.useState([]); + const [prevLayerPickInfo, setPrevLayerPickInfo] = React.useState([]); + + if (!isEqual(props.layerPickInfo, prevLayerPickInfo)) { + setPrevLayerPickInfo(props.layerPickInfo); + const newReadoutItems: ReadoutItem[] = []; + + if (props.layerPickInfo.length === 0) { + setInfoData([]); + return; + } + + const positionReadout = makePositionReadout(props.layerPickInfo[0]); + if (!positionReadout) { + return; + } + newReadoutItems.push(positionReadout); + + for (const layerPickInfo of props.layerPickInfo) { + const layerName = (layerPickInfo.layer?.props as unknown as ExtendedLayerProps)?.name; + const layerProps = layerPickInfo.properties; + + // pick info can have 2 types of properties that can be displayed on the info card + // 1. defined as propertyValue, used for general layer info (now using for positional data) + // 2. Another defined as array of property object described by type PropertyDataType + + const layerReadout = newReadoutItems.find((item) => item.label === layerName); + + // collecting card data for 1st type + const zValue = (layerPickInfo as LayerPickInfo).propertyValue; + if (zValue !== undefined) { + if (layerReadout) { + layerReadout.info.push({ + name: "Property value", + value: zValue, + }); + } else { + newReadoutItems.push({ + label: layerName ?? "Unknown layer", + info: [ + { + name: "Property value", + value: zValue, + }, + ], + }); + } + } + + // collecting card data for 2nd type + if (!layerProps || layerProps.length === 0) { + continue; + } + if (layerReadout) { + layerProps?.forEach((prop) => { + const property = layerReadout.info?.find((item) => item.name === prop.name); + if (property) { + property.value = prop.value; + } else { + layerReadout.info.push(prop); + } + }); + } else { + newReadoutItems.push({ + label: layerName ?? "Unknown layer", + info: layerProps, + }); + } + } + + setInfoData(newReadoutItems); + } + + if (!props.visible) { + return null; + } + + return ; +} diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx new file mode 100644 index 000000000..384b41bc5 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -0,0 +1,76 @@ +import React from "react"; + +import { Layer as DeckGlLayer } from "@deck.gl/core"; +import { SubsurfaceViewerWithCameraState } from "@modules/_shared/components/SubsurfaceViewerWithCameraState"; +import { BoundingBox2D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; + +import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; +import { Toolbar } from "./Toolbar"; + +export type ReadooutWrapperProps = { + views: ViewsType; + viewportAnnotations: React.ReactNode[]; + layers: DeckGlLayer[]; + bounds?: BoundingBox2D; +}; + +export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { + const id = React.useId(); + + const [cameraPositionSetByAction, setCameraPositionSetByAction] = React.useState(null); + const [triggerHomeCounter, setTriggerHomeCounter] = React.useState(0); + const [layerPickingInfo, setLayerPickingInfo] = React.useState([]); + + function handleFitInViewClick() { + setTriggerHomeCounter((prev) => prev + 1); + } + + function handleMouseHover(event: MapMouseEvent): void { + setLayerPickingInfo(event.infos); + } + + function handleMouseEvent(event: MapMouseEvent): void { + if (event.type === "hover") { + handleMouseHover(event); + } + } + + return ( + <> + + + setCameraPositionSetByAction(null)} + onMouseEvent={handleMouseEvent} + layers={props.layers} + scale={{ + visible: true, + incrementValue: 100, + widthPerUnit: 100, + cssStyle: { + right: 10, + top: 10, + }, + }} + coords={{ + visible: false, + multiPicking: true, + pickDepth: 2, + }} + triggerHome={triggerHomeCounter} + pickingRadius={5} + > + {props.viewportAnnotations} + + {props.views.viewports.length === 0 && ( +
+ Please add views and layers in the settings panel. +
+ )} + + ); +} diff --git a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx new file mode 100644 index 000000000..08527e27d --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx @@ -0,0 +1,21 @@ +import { Button } from "@lib/components/Button"; +import { Toolbar as GenericToolbar } from "@modules/_shared/components/Toolbar"; +import { FilterCenterFocus } from "@mui/icons-material"; + +export type ToolbarProps = { + onFitInView: () => void; +}; + +export function Toolbar(props: ToolbarProps): React.ReactNode { + function handleFitInViewClick() { + props.onFitInView(); + } + + return ( + + + + ); +} diff --git a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/AdvancedWellsLayer.ts b/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/AdvancedWellsLayer.ts new file mode 100644 index 000000000..112c3363b --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/AdvancedWellsLayer.ts @@ -0,0 +1,67 @@ +import { FilterContext, Layer, LayersList } from "@deck.gl/core"; +import { GeoJsonLayer } from "@deck.gl/layers"; +import { WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; + +export class AdvancedWellsLayer extends WellsLayer { + static layerName: string = "WellsLayer"; + + constructor(props: any) { + super(props); + } + + filterSubLayer(context: FilterContext): boolean { + if (context.layer.id.includes("names")) { + return context.viewport.zoom > -2; + } + + return true; + } + + renderLayers(): LayersList { + const layers = super.renderLayers(); + + if (!Array.isArray(layers)) { + return layers; + } + + const colorsLayer = layers.find((layer) => { + if (!(layer instanceof Layer)) { + return false; + } + + return layer.id.includes("colors"); + }); + + if (!(colorsLayer instanceof GeoJsonLayer)) { + return layers; + } + + const newColorsLayer = new GeoJsonLayer({ + data: colorsLayer.props.data, + pickable: true, + stroked: false, + positionFormat: colorsLayer.props.positionFormat, + pointRadiusUnits: "meters", + lineWidthUnits: "meters", + pointRadiusScale: this.props.pointRadiusScale, + lineWidthScale: this.props.lineWidthScale, + getLineWidth: colorsLayer.props.getLineWidth, + getPointRadius: colorsLayer.props.getPointRadius, + lineBillboard: true, + pointBillboard: true, + parameters: colorsLayer.props.parameters, + visible: colorsLayer.props.visible, + id: "colors", + lineWidthMinPixels: 1, + lineWidthMaxPixels: 5, + extensions: colorsLayer.props.extensions, + getDashArray: colorsLayer.props.getDashArray, + getLineColor: colorsLayer.props.getLineColor, + getFillColor: colorsLayer.props.getFillColor, + autoHighlight: true, + onHover: () => {}, + }); + + return [newColorsLayer, ...layers.filter((layer) => layer !== colorsLayer)]; + } +} diff --git a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/PlaceholderLayer.ts b/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/PlaceholderLayer.ts new file mode 100644 index 000000000..b146c9fea --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/PlaceholderLayer.ts @@ -0,0 +1,21 @@ +import { Layer } from "@deck.gl/core"; + +type PlaceholderLayerProps = { + id: string; +}; + +export class PlaceholderLayer extends Layer { + static layerName: string = "PlaceholderLayer"; + + constructor(props: PlaceholderLayerProps) { + super(props); + } + + initializeState(): void { + return; + } + + render() { + return null; + } +} diff --git a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts b/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts new file mode 100644 index 000000000..4c6ddd195 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts @@ -0,0 +1,121 @@ +import { CompositeLayer, CompositeLayerProps, FilterContext, Layer, UpdateParameters } from "@deck.gl/core"; +import { GeoJsonLayer, TextLayer } from "@deck.gl/layers"; + +import type { Feature, FeatureCollection } from "geojson"; + +export type WellBorePickLayerData = { + easting: number; + northing: number; + wellBoreUwi: string; + tvdMsl: number; + md: number; + slotName: string; +}; + +type TextLayerData = { + coordinates: [number, number, number]; + name: string; +}; + +export type WellBorePicksLayerProps = { + id: string; + data: WellBorePickLayerData[]; +}; + +export class WellborePicksLayer extends CompositeLayer { + static layerName: string = "WellborePicksLayer"; + private _textData: TextLayerData[] = []; + private _pointsData: FeatureCollection | null = null; + + filterSubLayer(context: FilterContext): boolean { + if (context.layer.id.includes("text")) { + return context.viewport.zoom > -4; + } + + return true; + } + + updateState(params: UpdateParameters>>): void { + const features: Feature[] = params.props.data.map((wellPick) => { + return { + type: "Feature", + geometry: { + type: "Point", + coordinates: [wellPick.easting, wellPick.northing], + }, + properties: { + name: `${wellPick.wellBoreUwi}, TVD_MSL: ${wellPick.tvdMsl}, MD: ${wellPick.md}`, + color: [100, 100, 100, 100], + }, + }; + }); + + const pointsData: FeatureCollection = { + type: "FeatureCollection", + features: features, + }; + + const textData: TextLayerData[] = this.props.data.map((wellPick) => { + return { + coordinates: [wellPick.easting, wellPick.northing, wellPick.tvdMsl], + name: wellPick.wellBoreUwi, + }; + }); + + this._pointsData = pointsData; + this._textData = textData; + } + + renderLayers() { + const fontSize = 16; + const sizeMinPixels = 16; + const sizeMaxPixels = 16; + + return [ + new GeoJsonLayer( + this.getSubLayerProps({ + id: "points", + data: this._pointsData ?? undefined, + filled: true, + lineWidthMinPixels: 5, + lineWidthMaxPixels: 5, + lineWidthUnits: "meters", + parameters: { + depthTest: false, + }, + getLineWidth: 1, + depthTest: false, + pickable: true, + getText: (d: Feature) => d.properties?.wellBoreUwi, + getLineColor: [50, 50, 50], + }) + ), + + new TextLayer( + this.getSubLayerProps({ + id: "text", + data: this._textData, + pickable: true, + getColor: [255, 255, 255], + fontWeight: 800, + fontSettings: { + fontSize: fontSize * 2, + sdf: true, + }, + outlineColor: [0, 0, 0], + outlineWidth: 2, + getSize: 12, + sdf: true, + sizeScale: fontSize, + sizeUnits: "meters", + sizeMinPixels: sizeMinPixels, + sizeMaxPixels: sizeMaxPixels, + getAlignmentBaseline: "top", + getTextAnchor: "middle", + getPosition: (d: TextLayerData) => d.coordinates, + getText: (d: TextLayerData) => d.name, + }) + ), + ]; + } +} diff --git a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts new file mode 100644 index 000000000..7e7f2329c --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts @@ -0,0 +1,335 @@ +import { PolygonData_api, SurfaceDataPng_api, SurfaceDef_api, WellborePick_api, WellboreTrajectory_api } from "@api"; +import { Layer } from "@deck.gl/core"; +import { GeoJsonLayer } from "@deck.gl/layers"; +import { defaultColorPalettes } from "@framework/utils/colorPalettes"; +import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; +import { Vec2, rotatePoint2Around } from "@lib/utils/vec2"; +import { GridMappedProperty_trans, GridSurface_trans } from "@modules/3DViewer/view/queries/queryDataTransforms"; +import { Layer as LayerInterface } from "@modules/_shared/LayerFramework/interfaces"; +import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; +import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; +import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; +import { ColormapLayer, Grid3DLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; + +import { Rgb, parse } from "culori"; +import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "geojson"; + +import { ObservedSurfaceLayer } from "../../LayerFramework/customLayerImplementations/ObservedSurfaceLayer"; +import { RealizationGridLayer } from "../../LayerFramework/customLayerImplementations/RealizationGridLayer"; +import { RealizationPolygonsLayer } from "../../LayerFramework/customLayerImplementations/RealizationPolygonsLayer"; +import { RealizationSurfaceLayer } from "../../LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; +import { StatisticalSurfaceLayer } from "../../LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; +import { AdvancedWellsLayer } from "../customDeckGlLayers/AdvancedWellsLayer"; +import { WellBorePickLayerData, WellborePicksLayer } from "../customDeckGlLayers/WellborePicksLayer"; + +export function makeDeckGlLayer(layer: LayerInterface, colorScale?: ColorScaleWithName): Layer | null { + const data = layer.getLayerDelegate().getData(); + + if (colorScale === undefined) { + colorScale = new ColorScaleWithName({ + colorPalette: defaultColorPalettes[0], + gradientType: ColorScaleGradientType.Sequential, + name: "Default", + type: ColorScaleType.Continuous, + steps: 10, + }); + } + + if (!data) { + return null; + } + if (layer instanceof ObservedSurfaceLayer) { + return createMapImageLayer( + data, + layer.getItemDelegate().getId(), + layer.getItemDelegate().getName(), + colorScale + ); + } + if (layer instanceof RealizationSurfaceLayer) { + return createMapImageLayer( + data, + layer.getItemDelegate().getId(), + layer.getItemDelegate().getName(), + colorScale + ); + } + if (layer instanceof StatisticalSurfaceLayer) { + return createMapImageLayer( + data, + layer.getItemDelegate().getId(), + layer.getItemDelegate().getName(), + colorScale + ); + } + if (layer instanceof RealizationPolygonsLayer) { + return createPolygonsLayer(data, layer.getItemDelegate().getId()); + } + if (layer instanceof DrilledWellTrajectoriesLayer) { + return makeWellsLayer(data, layer.getItemDelegate().getId(), null); + } + if (layer instanceof DrilledWellborePicksLayer) { + return createWellPicksLayer(data, layer.getItemDelegate().getId()); + } + if (layer instanceof RealizationGridLayer) { + return makeGrid3DLayer( + layer.getItemDelegate().getId(), + data.gridSurfaceData, + data.gridParameterData, + layer.getSettingsContext().getDelegate().getSettings().showGridLines.getDelegate().getValue(), + colorScale + ); + } + return null; +} +function createWellPicksLayer(wellPicksDataApi: WellborePick_api[], id: string): WellborePicksLayer { + const wellPicksData: WellBorePickLayerData[] = wellPicksDataApi.map((wellPick) => { + return { + easting: wellPick.easting, + northing: wellPick.northing, + wellBoreUwi: wellPick.uniqueWellboreIdentifier, + tvdMsl: wellPick.tvdMsl, + md: wellPick.md, + pickable: true, + slotName: "", + }; + }); + return new WellborePicksLayer({ + id: id, + data: wellPicksData, + pickable: true, + }); +} + +function createMapImageLayer( + layerData: SurfaceDataPng_api, + id: string, + name: string, + colorScale?: ColorScaleWithName +): ColormapLayer { + return new ColormapLayer({ + id: id, + name: name, + image: `data:image/png;base64,${layerData.png_image_base64}`, + bounds: calcBoundsForRotationAroundUpperLeftCorner(layerData.surface_def), + rotDeg: layerData.surface_def.rot_deg, + valueRange: [layerData.value_min, layerData.value_max], + colorMapRange: [layerData.value_min, layerData.value_max], + colorMapName: "Physics", + parameters: { + depthWriteEnabled: false, + }, + colorMapFunction: makeColorMapFunction(colorScale, layerData.value_min, layerData.value_max), + }); +} + +function calcBoundsForRotationAroundUpperLeftCorner(surfDef: SurfaceDef_api): [number, number, number, number] { + const width = (surfDef.npoints_x - 1) * surfDef.inc_x; + const height = (surfDef.npoints_y - 1) * surfDef.inc_y; + const orgRotPoint: Vec2 = { x: surfDef.origin_utm_x, y: surfDef.origin_utm_y }; + const orgTopLeft: Vec2 = { x: surfDef.origin_utm_x, y: surfDef.origin_utm_y + height }; + + const transTopLeft: Vec2 = rotatePoint2Around(orgTopLeft, orgRotPoint, (surfDef.rot_deg * Math.PI) / 180); + const tLeft = transTopLeft.x; + const tBottom = transTopLeft.y - height; + const tRight = transTopLeft.x + width; + const tTop = transTopLeft.y; + + const bounds: [number, number, number, number] = [tLeft, tBottom, tRight, tTop]; + + return bounds; +} + +function createPolygonsLayer(polygonsData: PolygonData_api[], id: string): GeoJsonLayer { + const features: Feature[] = polygonsData.map((polygon) => { + return polygonsToGeojson(polygon); + }); + const data: FeatureCollection = { + type: "FeatureCollection", + features: features, + }; + return new GeoJsonLayer({ + id: id, + data: data, + filled: false, + lineWidthMinPixels: 2, + parameters: { + depthTest: false, + }, + + pickable: true, + }); +} +function polygonsToGeojson(polygons: PolygonData_api): Feature { + const data: Feature = { + type: "Feature", + geometry: { + type: "Polygon", + coordinates: [zipCoords(polygons.x_arr, polygons.y_arr, polygons.z_arr)], + }, + properties: { name: polygons.poly_id, color: [0, 0, 0, 255] }, + }; + return data; +} + +function makeWellsLayer( + fieldWellboreTrajectoriesData: WellboreTrajectory_api[], + id: string, + selectedWellboreUuid: string | null +): WellsLayer { + const tempWorkingWellsData = fieldWellboreTrajectoriesData.filter( + (el) => el.uniqueWellboreIdentifier !== "NO 34/4-K-3 AH" + ); + const wellLayerDataFeatures = tempWorkingWellsData.map((well) => + wellTrajectoryToGeojson(well, selectedWellboreUuid) + ); + + function getLineStyleWidth(object: Feature): number { + if (object.properties && "lineWidth" in object.properties) { + return object.properties.lineWidth as number; + } + return 2; + } + + function getWellHeadStyleWidth(object: Feature): number { + if (object.properties && "wellHeadSize" in object.properties) { + return object.properties.wellHeadSize as number; + } + return 1; + } + + function getColor(object: Feature): [number, number, number, number] { + if (object.properties && "color" in object.properties) { + return object.properties.color as [number, number, number, number]; + } + return [50, 50, 50, 100]; + } + + const wellsLayer = new AdvancedWellsLayer({ + id: id, + data: { + type: "FeatureCollection", + unit: "m", + features: wellLayerDataFeatures, + }, + refine: false, + lineStyle: { width: getLineStyleWidth, color: getColor }, + wellHeadStyle: { size: getWellHeadStyleWidth, color: getColor }, + wellNameVisible: true, + pickable: true, + ZIncreasingDownwards: false, + outline: false, + lineWidthScale: 2, + }); + + return wellsLayer; +} + +function wellTrajectoryToGeojson( + wellTrajectory: WellboreTrajectory_api, + selectedWellboreUuid: string | null +): Record { + const point: Record = { + type: "Point", + coordinates: [wellTrajectory.eastingArr[0], wellTrajectory.northingArr[0], -wellTrajectory.tvdMslArr[0]], + }; + const coordinates: Record = { + type: "LineString", + coordinates: zipCoords(wellTrajectory.eastingArr, wellTrajectory.northingArr, wellTrajectory.tvdMslArr), + }; + + let color = [100, 100, 100]; + let lineWidth = 2; + let wellHeadSize = 1; + if (wellTrajectory.wellboreUuid === selectedWellboreUuid) { + color = [255, 0, 0]; + lineWidth = 5; + wellHeadSize = 10; + } + + const geometryCollection: Record = { + type: "Feature", + geometry: { + type: "GeometryCollection", + geometries: [point, coordinates], + }, + properties: { + uuid: wellTrajectory.wellboreUuid, + name: wellTrajectory.uniqueWellboreIdentifier, + uwi: wellTrajectory.uniqueWellboreIdentifier, + color, + md: [wellTrajectory.mdArr], + lineWidth, + wellHeadSize, + }, + }; + + return geometryCollection; +} + +function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { + const coords: number[][] = []; + for (let i = 0; i < xArr.length; i++) { + coords.push([xArr[i], yArr[i], -zArr[i]]); + } + + return coords; +} +type WorkingGrid3dLayer = { + pointsData: Float32Array; + polysData: Uint32Array; + propertiesData: Float32Array; + colorMapName: string; + ZIncreasingDownwards: boolean; +} & Layer; + +function makeGrid3DLayer( + id: string, + gridSurfaceData: GridSurface_trans, + gridParameterData: GridMappedProperty_trans, + showGridLines: boolean, + colorScale?: ColorScaleWithName +): WorkingGrid3dLayer { + const offsetXyz = [gridSurfaceData.origin_utm_x, gridSurfaceData.origin_utm_y, 0]; + const pointsNumberArray = gridSurfaceData.pointsFloat32Arr.map((val, i) => val + offsetXyz[i % 3]); + const polysNumberArray = gridSurfaceData.polysUint32Arr; + const grid3dLayer = new Grid3DLayer({ + id: id, + pointsData: pointsNumberArray, + polysData: polysNumberArray, + propertiesData: gridParameterData.polyPropsFloat32Arr, + ZIncreasingDownwards: false, + gridLines: showGridLines, + material: { ambient: 0.4, diffuse: 0.7, shininess: 8, specularColor: [25, 25, 25] }, + pickable: true, + colorMapName: "Physics", + colorMapClampColor: true, + colorMapRange: [gridParameterData.min_grid_prop_value, gridParameterData.max_grid_prop_value], + colorMapFunction: makeColorMapFunction( + colorScale, + gridParameterData.min_grid_prop_value, + gridParameterData.max_grid_prop_value + ), + }); + return grid3dLayer as unknown as WorkingGrid3dLayer; +} + +function makeColorMapFunction( + colorScale: ColorScaleWithName | undefined, + valueMin: number, + valueMax: number +): ((value: number) => [number, number, number]) | undefined { + if (!colorScale) { + return undefined; + } + + return (value: number) => { + const nonNormalizedValue = value * (valueMax - valueMin) + valueMin; + const interpolatedColor = colorScale.getColorForValue(nonNormalizedValue); + const color = parse(interpolatedColor) as Rgb; + if (color === undefined) { + return [0, 0, 0]; + } + return [color.r * 255, color.g * 255, color.b * 255]; + }; +} diff --git a/frontend/src/modules/3DViewerNew/view/utils/makeViewsAndLayers.ts b/frontend/src/modules/3DViewerNew/view/utils/makeViewsAndLayers.ts new file mode 100644 index 000000000..824c59cea --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/utils/makeViewsAndLayers.ts @@ -0,0 +1,183 @@ +import { Layer as DeckGlLayer } from "@deck.gl/core"; +import { StatusMessage } from "@framework/ModuleInstanceStatusController"; +import { defaultContinuousSequentialColorPalettes } from "@framework/utils/colorPalettes"; +import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; +import { GroupDelegate } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; +import { LayerColoringType, LayerStatus } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; +import { ColorScale } from "@modules/_shared/LayerFramework/framework/ColorScale/ColorScale"; +import { DeltaSurface } from "@modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurface"; +import { View } from "@modules/_shared/LayerFramework/framework/View/View"; +import { BoundingBox, Layer, instanceofGroup, instanceofLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; +import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; + +import { makeDeckGlLayer } from "./layerFactory"; + +export type DeckGlLayerWithPosition = { + layer: DeckGlLayer; + position: number; +}; + +export type DeckGlView = { + id: string; + color: string | null; + name: string; + layers: DeckGlLayerWithPosition[]; + colorScales: ColorScaleWithId[]; +}; + +export type DeckGlViewsAndLayers = { + views: DeckGlView[]; + layers: DeckGlLayerWithPosition[]; + errorMessages: (StatusMessage | string)[]; + boundingBox: BoundingBox | null; + colorScales: ColorScaleWithId[]; + numLoadingLayers: number; +}; + +export function recursivelyMakeViewsAndLayers( + groupDelegate: GroupDelegate, + numCollectedLayers: number = 0 +): DeckGlViewsAndLayers { + const collectedViews: DeckGlView[] = []; + const collectedLayers: DeckGlLayerWithPosition[] = []; + const collectedColorScales: ColorScaleWithId[] = []; + const collectedErrorMessages: (StatusMessage | string)[] = []; + let collectedNumLoadingLayers = 0; + let globalBoundingBox: BoundingBox | null = null; + + const children = groupDelegate.getChildren(); + + const maybeApplyBoundingBox = (boundingBox: BoundingBox | null) => { + if (boundingBox) { + globalBoundingBox = + globalBoundingBox === null ? boundingBox : makeNewBoundingBox(boundingBox, globalBoundingBox); + } + }; + + for (const child of children) { + if (!child.getItemDelegate().isVisible()) { + continue; + } + + if (instanceofGroup(child) && !(child instanceof DeltaSurface)) { + const { views, layers, boundingBox, colorScales, numLoadingLayers, errorMessages } = + recursivelyMakeViewsAndLayers(child.getGroupDelegate(), numCollectedLayers + collectedLayers.length); + + collectedErrorMessages.push(...errorMessages); + collectedNumLoadingLayers += numLoadingLayers; + maybeApplyBoundingBox(boundingBox); + + if (child instanceof View) { + const view: DeckGlView = { + id: child.getItemDelegate().getId(), + color: child.getGroupDelegate().getColor(), + name: child.getItemDelegate().getName(), + layers: layers, + colorScales, + }; + + collectedViews.push(view); + continue; + } + + collectedLayers.push(...layers); + collectedViews.push(...views); + } + + if (instanceofLayer(child)) { + if (child.getLayerDelegate().getStatus() === LayerStatus.LOADING) { + collectedNumLoadingLayers++; + } + + if (child.getLayerDelegate().getStatus() !== LayerStatus.SUCCESS) { + if (child.getLayerDelegate().getStatus() === LayerStatus.ERROR) { + const error = child.getLayerDelegate().getError(); + if (error) { + collectedErrorMessages.push(error); + } + } + continue; + } + + const colorScale = findColorScale(child); + + const layer = makeDeckGlLayer(child, colorScale?.colorScale ?? undefined); + + if (!layer) { + continue; + } + + if (colorScale) { + collectedColorScales.push(colorScale); + } + + const boundingBox = child.getLayerDelegate().getBoundingBox(); + maybeApplyBoundingBox(boundingBox); + collectedLayers.push({ layer, position: numCollectedLayers + collectedLayers.length }); + } + } + + return { + views: collectedViews, + layers: collectedLayers, + errorMessages: collectedErrorMessages, + boundingBox: globalBoundingBox, + colorScales: collectedColorScales, + numLoadingLayers: collectedNumLoadingLayers, + }; +} + +function findColorScale(layer: Layer): { id: string; colorScale: ColorScaleWithName } | null { + if (layer.getLayerDelegate().getColoringType() !== LayerColoringType.COLORSCALE) { + return null; + } + + let colorScaleWithName = new ColorScaleWithName({ + colorPalette: defaultContinuousSequentialColorPalettes[0], + gradientType: ColorScaleGradientType.Sequential, + name: layer.getItemDelegate().getName(), + type: ColorScaleType.Continuous, + steps: 10, + }); + + const range = layer.getLayerDelegate().getValueRange(); + if (range) { + colorScaleWithName.setRangeAndMidPoint(range[0], range[1], (range[0] + range[1]) / 2); + } + + const colorScaleItemArr = layer + .getItemDelegate() + .getParentGroup() + ?.getAncestorAndSiblingItems((item) => item instanceof ColorScale); + + if (colorScaleItemArr && colorScaleItemArr.length > 0) { + const colorScaleItem = colorScaleItemArr[0]; + if (colorScaleItem instanceof ColorScale) { + colorScaleWithName = ColorScaleWithName.fromColorScale( + colorScaleItem.getColorScale(), + layer.getItemDelegate().getName() + ); + + if (!colorScaleItem.getAreBoundariesUserDefined()) { + const range = layer.getLayerDelegate().getValueRange(); + if (range) { + colorScaleWithName.setRangeAndMidPoint(range[0], range[1], (range[0] + range[1]) / 2); + } + } + } + } + + return { + id: layer.getItemDelegate().getId(), + colorScale: colorScaleWithName, + }; +} + +function makeNewBoundingBox(newBoundingBox: BoundingBox, oldBoundingBox: BoundingBox): BoundingBox { + return { + x: [Math.min(newBoundingBox.x[0], oldBoundingBox.x[0]), Math.max(newBoundingBox.x[1], oldBoundingBox.x[1])], + y: [Math.min(newBoundingBox.y[0], oldBoundingBox.y[0]), Math.max(newBoundingBox.y[1], oldBoundingBox.y[1])], + z: [Math.min(newBoundingBox.z[0], oldBoundingBox.z[0]), Math.max(newBoundingBox.z[1], oldBoundingBox.z[1])], + }; +} diff --git a/frontend/src/modules/3DViewerNew/view/view.tsx b/frontend/src/modules/3DViewerNew/view/view.tsx new file mode 100644 index 000000000..bc4022d72 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/view.tsx @@ -0,0 +1,24 @@ +import React from "react"; + +import { ModuleViewProps } from "@framework/Module"; + +import { LayersWrapper } from "./components/LayersWrapper"; + +import { Interfaces } from "../interfaces"; + +export function View(props: ModuleViewProps): React.ReactNode { + const preferredViewLayout = props.viewContext.useSettingsToViewInterfaceValue("preferredViewLayout"); + const layerManager = props.viewContext.useSettingsToViewInterfaceValue("layerManager"); + + if (!layerManager) { + return null; + } + + return ( + + ); +} diff --git a/frontend/src/modules/registerAllModules.ts b/frontend/src/modules/registerAllModules.ts index f79e624aa..bb682c8f8 100644 --- a/frontend/src/modules/registerAllModules.ts +++ b/frontend/src/modules/registerAllModules.ts @@ -2,6 +2,7 @@ import { isDevMode } from "@lib/utils/devMode"; import "./2DViewer/registerModule"; import "./3DViewer/registerModule"; +import "./3DViewerNew/registerModule"; import "./DistributionPlot/registerModule"; import "./FlowNetwork/registerModule"; import "./InplaceVolumetricsPlot/registerModule"; @@ -15,9 +16,9 @@ import "./SimulationTimeSeries/registerModule"; import "./SimulationTimeSeriesSensitivity/registerModule"; import "./SubsurfaceMap/registerModule"; import "./TornadoChart/registerModule"; -import "./WellLogViewer/registerModule"; import "./Vfp/registerModule"; import "./WellCompletions/registerModule"; +import "./WellLogViewer/registerModule"; if (isDevMode()) { await import("./MyModule/registerModule"); From 65abe08079c08a519e9d5af0dd0aae8cad935035 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 15 Jan 2025 17:09:45 +0100 Subject: [PATCH 02/97] wip --- frontend/src/lib/utils/geometry.ts | 24 +- .../ObservedSurfaceLayer.ts | 124 -------- .../ObservedSurfaceSettingsContext.ts | 137 --------- .../ObservedSurfaceLayer/index.ts | 2 - .../ObservedSurfaceLayer/types.ts | 9 - .../RealizationPolygonsLayer.ts | 121 -------- .../RealizationPolygonsSettingsContext.ts | 116 -------- .../RealizationPolygonsLayer/index.ts | 2 - .../RealizationPolygonsLayer/types.ts | 9 - .../RealizationSurfaceLayer.ts | 130 --------- .../RealizationSurfaceSettingsContext.ts | 152 ---------- .../RealizationSurfaceLayer/index.ts | 2 - .../RealizationSurfaceLayer/types.ts | 10 - .../StatisticalSurfaceLayer.ts | 155 ---------- .../StatisticalSurfaceSettingsContext.ts | 169 ----------- .../StatisticalSurfaceLayer/index.ts | 2 - .../StatisticalSurfaceLayer/types.ts | 13 - .../layerManagerComponentWrapper.tsx | 63 +---- .../view/components/LayersWrapper.tsx | 28 +- .../view/components/ReadoutWrapper.tsx | 39 ++- .../3DViewerNew/view/components/Toolbar.tsx | 50 +++- .../EditablePolylineLayer.ts | 264 ++++++++++++++++++ .../3DViewerNew/view/utils/layerFactory.ts | 110 +------- .../_shared/components/Toolbar/toolbar.tsx | 2 +- .../components/Toolbar/toolbarDivider.tsx | 2 +- 25 files changed, 396 insertions(+), 1339 deletions(-) delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/index.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/index.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/types.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/index.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts create mode 100644 frontend/src/modules/3DViewerNew/view/customDeckGlLayers/EditablePolylineLayer.ts diff --git a/frontend/src/lib/utils/geometry.ts b/frontend/src/lib/utils/geometry.ts index 4ec4291c1..96f17477a 100644 --- a/frontend/src/lib/utils/geometry.ts +++ b/frontend/src/lib/utils/geometry.ts @@ -12,6 +12,15 @@ export type Rect2D = { height: number; }; +export type Rect3D = { + x: number; + y: number; + z: number; + width: number; + height: number; + depth: number; +}; + export const ORIGIN = Object.freeze({ x: 0, y: 0 }); export const MANHATTAN_LENGTH = 13.11; @@ -78,7 +87,20 @@ export function addMarginToRect(rect: Rect2D, margin: number): Rect2D { }; } -export function outerRectContainsInnerRect(outerRect: Rect2D, innerRect: Rect2D): boolean { +export function outerRectContainsInnerRect(outerRect: Rect3D, innerRect: Rect3D): boolean; +export function outerRectContainsInnerRect(outerRect: Rect2D, innerRect: Rect2D): boolean; +export function outerRectContainsInnerRect(outerRect: Rect2D | Rect3D, innerRect: Rect2D | Rect3D): boolean { + if ("depth" in outerRect && "depth" in innerRect) { + return ( + outerRect.x <= innerRect.x && + outerRect.y <= innerRect.y && + outerRect.z <= innerRect.z && + outerRect.x + outerRect.width >= innerRect.x + innerRect.width && + outerRect.y + outerRect.height >= innerRect.y + innerRect.height && + outerRect.z + outerRect.depth >= innerRect.z + innerRect.depth + ); + } + return ( outerRect.x <= innerRect.x && outerRect.y <= innerRect.y && diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts deleted file mode 100644 index 7d48713b8..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { SurfaceDataPng_api, getSurfaceDataOptions } from "@api"; -import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; -import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { FullSurfaceAddress, SurfaceAddressBuilder } from "@modules/_shared/Surface"; -import { SurfaceDataFloat_trans, transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; -import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; -import { QueryClient } from "@tanstack/react-query"; - -import { isEqual } from "lodash"; - -import { ObservedSurfaceSettingsContext } from "./ObservedSurfaceSettingsContext"; -import { ObservedSurfaceSettings } from "./types"; - -export class ObservedSurfaceLayer - implements Layer -{ - private _layerDelegate: LayerDelegate; - private _itemDelegate: ItemDelegate; - - constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Observed Surface", layerManager); - this._layerDelegate = new LayerDelegate( - this, - layerManager, - new ObservedSurfaceSettingsContext(layerManager), - LayerColoringType.COLORSCALE - ); - } - - getSettingsContext() { - return this._layerDelegate.getSettingsContext(); - } - - getItemDelegate(): ItemDelegate { - return this._itemDelegate; - } - - getLayerDelegate(): LayerDelegate { - return this._layerDelegate; - } - - doSettingsChangesRequireDataRefetch( - prevSettings: ObservedSurfaceSettings, - newSettings: ObservedSurfaceSettings - ): boolean { - return !isEqual(prevSettings, newSettings); - } - - makeBoundingBox(): BoundingBox | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return { - x: [data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.max_x], - y: [data.transformed_bbox_utm.min_y, data.transformed_bbox_utm.max_y], - z: [0, 0], - }; - } - - makeValueRange(): [number, number] | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return [data.value_min, data.value_max]; - } - - fetchData(queryClient: QueryClient): Promise { - let surfaceAddress: FullSurfaceAddress | null = null; - const addrBuilder = new SurfaceAddressBuilder(); - - const settings = this.getSettingsContext().getDelegate().getSettings(); - const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); - const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); - const attribute = settings[SettingType.SURFACE_ATTRIBUTE].getDelegate().getValue(); - const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - - if (ensembleIdent && surfaceName && attribute && timeOrInterval) { - addrBuilder.withEnsembleIdent(ensembleIdent); - addrBuilder.withName(surfaceName); - addrBuilder.withAttribute(attribute); - addrBuilder.withTimeOrInterval(timeOrInterval); - - surfaceAddress = addrBuilder.buildObservedAddress(); - } - - const surfAddrStr = surfaceAddress ? encodeSurfAddrStr(surfaceAddress) : null; - - const queryKey = ["getSurfaceData", surfAddrStr, null, "png"]; - - this._layerDelegate.registerQueryKey(queryKey); - - const promise = queryClient - .fetchQuery({ - ...getSurfaceDataOptions({ - query: { - surf_addr_str: surfAddrStr ?? "", - data_format: "png", - resample_to_def_str: null, - }, - }), - }) - .then((data) => transformSurfaceData(data)); - - return promise; - } - - serializeState(): SerializedLayer { - return this._layerDelegate.serializeState(); - } - - deserializeState(serializedState: SerializedLayer): void { - this._layerDelegate.deserializeState(serializedState); - } -} - -LayerRegistry.registerLayer(ObservedSurfaceLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts deleted file mode 100644 index da58be5cf..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { SurfaceTimeType_api, getObservedSurfacesMetadataOptions } from "@api"; -import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; -import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; -import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; -import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -import { ObservedSurfaceSettings } from "./types"; - -export class ObservedSurfaceSettingsContext implements SettingsContext { - private _contextDelegate: SettingsContextDelegate; - - constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate( - this, - layerManager, - { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.SURFACE_ATTRIBUTE]: new SurfaceAttributeSetting(), - [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - } - ); - } - - getDelegate(): SettingsContextDelegate { - return this._contextDelegate; - } - - getSettings() { - return this._contextDelegate.getSettings(); - } - defineDependencies({ - helperDependency, - availableSettingsUpdater, - workbenchSession, - queryClient, - }: DefineDependenciesArgs) { - availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { - const fieldIdentifier = getGlobalSetting("fieldId"); - const ensembleSet = workbenchSession.getEnsembleSet(); - - const ensembleIdents = ensembleSet - .getRegularEnsembleArray() - .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) - .map((ensemble) => ensemble.getIdent()); - - return ensembleIdents; - }); - - const observedSurfaceMetadataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - - if (!ensembleIdent) { - return null; - } - - return await queryClient.fetchQuery({ - ...getObservedSurfacesMetadataOptions({ - query: { - case_uuid: ensembleIdent.getCaseUuid(), - }, - signal: abortSignal, - }), - }); - }); - - availableSettingsUpdater(SettingType.SURFACE_ATTRIBUTE, ({ getHelperDependency }) => { - const data = getHelperDependency(observedSurfaceMetadataDep); - - if (!data) { - return []; - } - - const availableAttributes = [ - ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), - ]; - - return availableAttributes; - }); - - availableSettingsUpdater(SettingType.SURFACE_NAME, ({ getHelperDependency, getLocalSetting }) => { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); - const data = getHelperDependency(observedSurfaceMetadataDep); - - if (!attribute || !data) { - return []; - } - - const availableSurfaceNames = [ - ...Array.from( - new Set( - data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name) - ) - ), - ]; - - return availableSurfaceNames; - }); - - availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); - const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); - const data = getHelperDependency(observedSurfaceMetadataDep); - - if (!attribute || !surfaceName || !data) { - return []; - } - - const availableTimeOrIntervals: string[] = []; - const availableTimeTypes = [ - ...Array.from( - new Set( - data.surfaces - .filter((surface) => surface.attribute_name === attribute && surface.name === surfaceName) - .map((el) => el.time_type) - ) - ), - ]; - - if (availableTimeTypes.includes(SurfaceTimeType_api.NO_TIME)) { - availableTimeOrIntervals.push(SurfaceTimeType_api.NO_TIME); - } - if (availableTimeTypes.includes(SurfaceTimeType_api.TIME_POINT)) { - availableTimeOrIntervals.push(...data.time_points_iso_str); - } - if (availableTimeTypes.includes(SurfaceTimeType_api.INTERVAL)) { - availableTimeOrIntervals.push(...data.time_intervals_iso_str); - } - - return availableTimeOrIntervals; - }); - } -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/index.ts deleted file mode 100644 index 5309f6604..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { ObservedSurfaceLayer } from "./ObservedSurfaceLayer"; -export { ObservedSurfaceSettingsContext } from "./ObservedSurfaceSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts deleted file mode 100644 index 37abb48dc..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -export type ObservedSurfaceSettings = { - [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; - [SettingType.SURFACE_ATTRIBUTE]: string | null; - [SettingType.SURFACE_NAME]: string | null; - [SettingType.TIME_OR_INTERVAL]: string | null; -}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts deleted file mode 100644 index ed3d45891..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { PolygonData_api, getPolygonsDataOptions } from "@api"; -import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; -import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { QueryClient } from "@tanstack/react-query"; - -import { isEqual } from "lodash"; - -import { RealizationPolygonsSettingsContext } from "./RealizationPolygonsSettingsContext"; -import { RealizationPolygonsSettings } from "./types"; - -export class RealizationPolygonsLayer implements Layer { - private _layerDelegate: LayerDelegate; - private _itemDelegate: ItemDelegate; - - constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Realization Polygons", layerManager); - this._layerDelegate = new LayerDelegate( - this, - layerManager, - new RealizationPolygonsSettingsContext(layerManager), - LayerColoringType.NONE - ); - } - - getSettingsContext() { - return this._layerDelegate.getSettingsContext(); - } - - getItemDelegate(): ItemDelegate { - return this._itemDelegate; - } - - getLayerDelegate(): LayerDelegate { - return this._layerDelegate; - } - - doSettingsChangesRequireDataRefetch( - prevSettings: RealizationPolygonsSettings, - newSettings: RealizationPolygonsSettings - ): boolean { - return !isEqual(prevSettings, newSettings); - } - - makeBoundingBox(): BoundingBox | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - const bbox: BoundingBox = { - x: [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY], - y: [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY], - z: [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY], - }; - - for (const polygon of data) { - for (const point of polygon.x_arr) { - bbox.x[0] = Math.min(bbox.x[0], point); - bbox.x[1] = Math.max(bbox.x[1], point); - } - for (const point of polygon.y_arr) { - bbox.y[0] = Math.min(bbox.y[0], point); - bbox.y[1] = Math.max(bbox.y[1], point); - } - for (const point of polygon.z_arr) { - bbox.z[0] = Math.min(bbox.z[0], point); - bbox.z[1] = Math.max(bbox.z[1], point); - } - } - - return bbox; - } - - fetchData(queryClient: QueryClient): Promise { - const settings = this.getSettingsContext().getDelegate().getSettings(); - const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); - const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); - const polygonsName = settings[SettingType.POLYGONS_NAME].getDelegate().getValue(); - const polygonsAttribute = settings[SettingType.POLYGONS_ATTRIBUTE].getDelegate().getValue(); - - const queryOptions = getPolygonsDataOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - realization_num: realizationNum ?? 0, - name: polygonsName ?? "", - attribute: polygonsAttribute ?? "", - }, - }); - - this._layerDelegate.registerQueryKey(queryOptions.queryKey); - - const promise = queryClient.fetchQuery({ - ...getPolygonsDataOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - realization_num: realizationNum ?? 0, - name: polygonsName ?? "", - attribute: polygonsAttribute ?? "", - }, - }), - }); - - return promise; - } - - serializeState(): SerializedLayer { - return this._layerDelegate.serializeState(); - } - - deserializeState(serializedState: SerializedLayer): void { - this._layerDelegate.deserializeState(serializedState); - } -} - -LayerRegistry.registerLayer(RealizationPolygonsLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts deleted file mode 100644 index fdd1ce3c6..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { getPolygonsDirectoryOptions } from "@api"; -import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; -import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { PolygonsAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/PolygonsAttributeSetting"; -import { PolygonsNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/PolygonsNameSetting"; -import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -import { RealizationPolygonsSettings } from "./types"; - -export class RealizationPolygonsSettingsContext implements SettingsContext { - private _contextDelegate: SettingsContextDelegate; - - constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - RealizationPolygonsSettings, - keyof RealizationPolygonsSettings - >(this, layerManager, { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.POLYGONS_ATTRIBUTE]: new PolygonsAttributeSetting(), - [SettingType.POLYGONS_NAME]: new PolygonsNameSetting(), - }); - } - - getDelegate(): SettingsContextDelegate { - return this._contextDelegate; - } - - getSettings() { - return this._contextDelegate.getSettings(); - } - - defineDependencies({ - helperDependency, - availableSettingsUpdater, - queryClient, - }: DefineDependenciesArgs) { - availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { - const fieldIdentifier = getGlobalSetting("fieldId"); - const ensembles = getGlobalSetting("ensembles"); - - const ensembleIdents = ensembles - .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) - .map((ensemble) => ensemble.getIdent()); - - return ensembleIdents; - }); - - availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); - - if (!ensembleIdent) { - return []; - } - - const realizations = realizationFilterFunc(ensembleIdent); - - return [...realizations]; - }); - - const realizationPolygonsMetadataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - - if (!ensembleIdent) { - return null; - } - - return await queryClient.fetchQuery({ - ...getPolygonsDirectoryOptions({ - query: { - case_uuid: ensembleIdent.getCaseUuid(), - ensemble_name: ensembleIdent.getEnsembleName(), - }, - signal: abortSignal, - }), - }); - }); - - availableSettingsUpdater(SettingType.POLYGONS_ATTRIBUTE, ({ getHelperDependency }) => { - const data = getHelperDependency(realizationPolygonsMetadataDep); - - if (!data) { - return []; - } - - const availableAttributes = [ - ...Array.from(new Set(data.map((polygonsMeta) => polygonsMeta.attribute_name))), - ]; - - return availableAttributes; - }); - - availableSettingsUpdater(SettingType.POLYGONS_NAME, ({ getHelperDependency, getLocalSetting }) => { - const attribute = getLocalSetting(SettingType.POLYGONS_ATTRIBUTE); - const data = getHelperDependency(realizationPolygonsMetadataDep); - - if (!attribute || !data) { - return []; - } - - const availableSurfaceNames = [ - ...Array.from( - new Set( - data.filter((polygonsMeta) => polygonsMeta.attribute_name === attribute).map((el) => el.name) - ) - ), - ]; - - return availableSurfaceNames; - }); - } -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/index.ts deleted file mode 100644 index 6e24a10fa..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { RealizationPolygonsLayer } from "./RealizationPolygonsLayer"; -export { RealizationPolygonsSettingsContext } from "./RealizationPolygonsSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/types.ts deleted file mode 100644 index d18b2ac32..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -export type RealizationPolygonsSettings = { - [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; - [SettingType.REALIZATION]: number | null; - [SettingType.POLYGONS_ATTRIBUTE]: string | null; - [SettingType.POLYGONS_NAME]: string | null; -}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts deleted file mode 100644 index 52be9cf8c..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { SurfaceDataPng_api, SurfaceTimeType_api } from "@api"; -import { getSurfaceDataOptions } from "@api"; -import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; -import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { FullSurfaceAddress, SurfaceAddressBuilder } from "@modules/_shared/Surface"; -import { SurfaceDataFloat_trans, transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; -import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; -import { QueryClient } from "@tanstack/react-query"; - -import { isEqual } from "lodash"; - -import { RealizationSurfaceSettingsContext } from "./RealizationSurfaceSettingsContext"; -import { RealizationSurfaceSettings } from "./types"; - -export class RealizationSurfaceLayer - implements Layer -{ - private _layerDelegate: LayerDelegate; - private _itemDelegate: ItemDelegate; - - constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Realization Surface", layerManager); - this._layerDelegate = new LayerDelegate( - this, - layerManager, - new RealizationSurfaceSettingsContext(layerManager), - LayerColoringType.COLORSCALE - ); - } - - getSettingsContext() { - return this._layerDelegate.getSettingsContext(); - } - - getItemDelegate(): ItemDelegate { - return this._itemDelegate; - } - - getLayerDelegate(): LayerDelegate { - return this._layerDelegate; - } - - doSettingsChangesRequireDataRefetch( - prevSettings: RealizationSurfaceSettings, - newSettings: RealizationSurfaceSettings - ): boolean { - return !isEqual(prevSettings, newSettings); - } - - makeBoundingBox(): BoundingBox | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return { - x: [data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.max_x], - y: [data.transformed_bbox_utm.min_y, data.transformed_bbox_utm.max_y], - z: [0, 0], - }; - } - - makeValueRange(): [number, number] | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return [data.value_min, data.value_max]; - } - - fetchData(queryClient: QueryClient): Promise { - let surfaceAddress: FullSurfaceAddress | null = null; - const addrBuilder = new SurfaceAddressBuilder(); - - const settings = this.getSettingsContext().getDelegate().getSettings(); - const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); - const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); - const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); - const attribute = settings[SettingType.SURFACE_ATTRIBUTE].getDelegate().getValue(); - const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - - if (ensembleIdent && surfaceName && attribute && realizationNum !== null) { - addrBuilder.withEnsembleIdent(ensembleIdent); - addrBuilder.withName(surfaceName); - addrBuilder.withAttribute(attribute); - addrBuilder.withRealization(realizationNum); - - if (timeOrInterval !== SurfaceTimeType_api.NO_TIME) { - addrBuilder.withTimeOrInterval(timeOrInterval); - } - - surfaceAddress = addrBuilder.buildRealizationAddress(); - } - - const surfAddrStr = surfaceAddress ? encodeSurfAddrStr(surfaceAddress) : null; - - const queryKey = ["getSurfaceData", surfAddrStr, null, "png"]; - - this._layerDelegate.registerQueryKey(queryKey); - - const promise = queryClient - .fetchQuery({ - ...getSurfaceDataOptions({ - query: { - surf_addr_str: surfAddrStr ?? "", - data_format: "png", - resample_to_def_str: null, - }, - }), - }) - .then((data) => transformSurfaceData(data)); - - return promise; - } - - serializeState(): SerializedLayer { - return this._layerDelegate.serializeState(); - } - - deserializeState(serializedState: SerializedLayer): void { - this._layerDelegate.deserializeState(serializedState); - } -} - -LayerRegistry.registerLayer(RealizationSurfaceLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts deleted file mode 100644 index 9829608da..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { SurfaceTimeType_api } from "@api"; -import { getRealizationSurfacesMetadataOptions } from "@api"; -import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; -import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; -import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; -import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -import { RealizationSurfaceSettings } from "./types"; - -export class RealizationSurfaceSettingsContext implements SettingsContext { - private _contextDelegate: SettingsContextDelegate; - - constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - RealizationSurfaceSettings, - keyof RealizationSurfaceSettings - >(this, layerManager, { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.SURFACE_ATTRIBUTE]: new SurfaceAttributeSetting(), - [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - }); - } - - getDelegate(): SettingsContextDelegate { - return this._contextDelegate; - } - - getSettings() { - return this._contextDelegate.getSettings(); - } - - defineDependencies({ - helperDependency, - availableSettingsUpdater, - queryClient, - }: DefineDependenciesArgs) { - availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { - const fieldIdentifier = getGlobalSetting("fieldId"); - const ensembles = getGlobalSetting("ensembles"); - - const ensembleIdents = ensembles - .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) - .map((ensemble) => ensemble.getIdent()); - - return ensembleIdents; - }); - - availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); - - if (!ensembleIdent) { - return []; - } - - const realizations = realizationFilterFunc(ensembleIdent); - - return [...realizations]; - }); - - const realizationSurfaceMetadataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - - if (!ensembleIdent) { - return null; - } - - return await queryClient.fetchQuery({ - ...getRealizationSurfacesMetadataOptions({ - query: { - case_uuid: ensembleIdent.getCaseUuid(), - ensemble_name: ensembleIdent.getEnsembleName(), - }, - signal: abortSignal, - }), - }); - }); - - availableSettingsUpdater(SettingType.SURFACE_ATTRIBUTE, ({ getHelperDependency }) => { - const data = getHelperDependency(realizationSurfaceMetadataDep); - - if (!data) { - return []; - } - - const availableAttributes = [ - ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), - ]; - - return availableAttributes; - }); - - availableSettingsUpdater(SettingType.SURFACE_NAME, ({ getHelperDependency, getLocalSetting }) => { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); - const data = getHelperDependency(realizationSurfaceMetadataDep); - - if (!attribute || !data) { - return []; - } - - const availableSurfaceNames = [ - ...Array.from( - new Set( - data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name) - ) - ), - ]; - - return availableSurfaceNames; - }); - - availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); - const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); - const data = getHelperDependency(realizationSurfaceMetadataDep); - - if (!attribute || !surfaceName || !data) { - return []; - } - - const availableTimeOrIntervals: string[] = []; - const availableTimeTypes = [ - ...Array.from( - new Set( - data.surfaces - .filter((surface) => surface.attribute_name === attribute && surface.name === surfaceName) - .map((el) => el.time_type) - ) - ), - ]; - - if (availableTimeTypes.includes(SurfaceTimeType_api.NO_TIME)) { - availableTimeOrIntervals.push(SurfaceTimeType_api.NO_TIME); - } - if (availableTimeTypes.includes(SurfaceTimeType_api.TIME_POINT)) { - availableTimeOrIntervals.push(...data.time_points_iso_str); - } - if (availableTimeTypes.includes(SurfaceTimeType_api.INTERVAL)) { - availableTimeOrIntervals.push(...data.time_intervals_iso_str); - } - - return availableTimeOrIntervals; - }); - } -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts deleted file mode 100644 index ed6642a0d..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { RealizationSurfaceLayer } from "./RealizationSurfaceLayer"; -export { RealizationSurfaceSettingsContext } from "./RealizationSurfaceSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts deleted file mode 100644 index c0225d599..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -export type RealizationSurfaceSettings = { - [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; - [SettingType.REALIZATION]: number | null; - [SettingType.SURFACE_ATTRIBUTE]: string | null; - [SettingType.SURFACE_NAME]: string | null; - [SettingType.TIME_OR_INTERVAL]: string | null; -}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts deleted file mode 100644 index 28541edef..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { SurfaceDataPng_api, SurfaceTimeType_api, getSurfaceDataOptions } from "@api"; -import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; -import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { FullSurfaceAddress, SurfaceAddressBuilder } from "@modules/_shared/Surface"; -import { SurfaceDataFloat_trans, transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; -import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; -import { QueryClient } from "@tanstack/react-query"; - -import { isEqual } from "lodash"; - -import { StatisticalSurfaceSettingsContext } from "./StatisticalSurfaceSettingsContext"; -import { StatisticalSurfaceSettings } from "./types"; - -export class StatisticalSurfaceLayer - implements Layer -{ - private _itemDelegate: ItemDelegate; - private _layerDelegate: LayerDelegate; - - constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Statistical Surface", layerManager); - this._layerDelegate = new LayerDelegate( - this, - layerManager, - new StatisticalSurfaceSettingsContext(layerManager), - LayerColoringType.COLORSCALE - ); - } - - getSettingsContext() { - return this._layerDelegate.getSettingsContext(); - } - - getItemDelegate(): ItemDelegate { - return this._itemDelegate; - } - - getLayerDelegate(): LayerDelegate { - return this._layerDelegate; - } - - doSettingsChangesRequireDataRefetch( - prevSettings: StatisticalSurfaceSettings, - newSettings: StatisticalSurfaceSettings - ): boolean { - return !isEqual(prevSettings, newSettings); - } - - makeBoundingBox(): BoundingBox | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return { - x: [data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.max_x], - y: [data.transformed_bbox_utm.min_y, data.transformed_bbox_utm.max_y], - z: [0, 0], - }; - } - - makeValueRange(): [number, number] | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return [data.value_min, data.value_max]; - } - - fetchData(queryClient: QueryClient): Promise { - let surfaceAddress: FullSurfaceAddress | null = null; - const addrBuilder = new SurfaceAddressBuilder(); - const workbenchSession = this.getLayerDelegate().getLayerManager().getWorkbenchSession(); - const settings = this.getSettingsContext().getDelegate().getSettings(); - const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); - const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); - const attribute = settings[SettingType.SURFACE_ATTRIBUTE].getDelegate().getValue(); - const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - const statisticFunction = settings[SettingType.STATISTIC_FUNCTION].getDelegate().getValue(); - const sensitivityNameCasePair = settings[SettingType.SENSITIVITY].getDelegate().getValue(); - - if (ensembleIdent && surfaceName && attribute) { - addrBuilder.withEnsembleIdent(ensembleIdent); - addrBuilder.withName(surfaceName); - addrBuilder.withAttribute(attribute); - - // Get filtered realizations from workbench - let filteredRealizations = workbenchSession - .getRealizationFilterSet() - .getRealizationFilterForEnsembleIdent(ensembleIdent) - .getFilteredRealizations(); - const currentEnsemble = workbenchSession.getEnsembleSet().findEnsemble(ensembleIdent); - - // If sensitivity is set, filter realizations further to only include the realizations that are in the sensitivity - if (sensitivityNameCasePair) { - const sensitivity = currentEnsemble - ?.getSensitivities() - ?.getCaseByName(sensitivityNameCasePair.sensitivityName, sensitivityNameCasePair.sensitivityCase); - - const sensitivityRealizations = sensitivity?.realizations ?? []; - - filteredRealizations = filteredRealizations.filter((realization) => - sensitivityRealizations.includes(realization) - ); - } - - // If realizations are filtered, update the address - const allRealizations = currentEnsemble?.getRealizations() ?? []; - if (!isEqual([...allRealizations], [...filteredRealizations])) { - addrBuilder.withStatisticRealizations([...filteredRealizations]); - } - - if (timeOrInterval !== SurfaceTimeType_api.NO_TIME) { - addrBuilder.withTimeOrInterval(timeOrInterval); - } - addrBuilder.withStatisticFunction(statisticFunction); - surfaceAddress = addrBuilder.buildStatisticalAddress(); - } - - const surfAddrStr = surfaceAddress ? encodeSurfAddrStr(surfaceAddress) : null; - - const queryOptions = getSurfaceDataOptions({ - query: { - surf_addr_str: surfAddrStr ?? "", - data_format: "png", - resample_to_def_str: null, - }, - }); - - this._layerDelegate.registerQueryKey(queryOptions.queryKey); - - const promise = queryClient - .fetchQuery({ - ...queryOptions, - }) - .then((data) => transformSurfaceData(data)); - - return promise; - } - - serializeState(): SerializedLayer { - return this._layerDelegate.serializeState(); - } - - deserializeState(serializedState: SerializedLayer): void { - this._layerDelegate.deserializeState(serializedState); - } -} - -LayerRegistry.registerLayer(StatisticalSurfaceLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts deleted file mode 100644 index 604ae0c28..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { SurfaceStatisticFunction_api, SurfaceTimeType_api } from "@api"; -import { getRealizationSurfacesMetadataOptions } from "@api"; -import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; -import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { - SensitivityNameCasePair, - SensitivitySetting, -} from "@modules/_shared/LayerFramework/settings/implementations/SensitivitySetting"; -import { StatisticFunctionSetting } from "@modules/_shared/LayerFramework/settings/implementations/StatisticFunctionSetting"; -import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; -import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; -import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -import { StatisticalSurfaceSettings } from "./types"; - -export class StatisticalSurfaceSettingsContext implements SettingsContext { - private _contextDelegate: SettingsContextDelegate; - - constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - StatisticalSurfaceSettings, - keyof StatisticalSurfaceSettings - >(this, layerManager, { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.STATISTIC_FUNCTION]: new StatisticFunctionSetting(), - [SettingType.SENSITIVITY]: new SensitivitySetting(), - [SettingType.SURFACE_ATTRIBUTE]: new SurfaceAttributeSetting(), - [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - }); - } - - getDelegate(): SettingsContextDelegate { - return this._contextDelegate; - } - - getSettings() { - return this._contextDelegate.getSettings(); - } - - defineDependencies({ - helperDependency, - availableSettingsUpdater, - workbenchSession, - queryClient, - }: DefineDependenciesArgs) { - availableSettingsUpdater(SettingType.STATISTIC_FUNCTION, () => Object.values(SurfaceStatisticFunction_api)); - availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { - const fieldIdentifier = getGlobalSetting("fieldId"); - const ensembles = getGlobalSetting("ensembles"); - - const ensembleIdents = ensembles - .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) - .map((ensemble) => ensemble.getIdent()); - - return ensembleIdents; - }); - availableSettingsUpdater(SettingType.SENSITIVITY, ({ getLocalSetting }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - - if (!ensembleIdent) { - return []; - } - - const ensembleSet = workbenchSession.getEnsembleSet(); - const currentEnsemble = ensembleSet.findEnsemble(ensembleIdent); - const sensitivities = currentEnsemble?.getSensitivities()?.getSensitivityArr() ?? []; - if (sensitivities.length === 0) { - return []; - } - const availableSensitivityPairs: SensitivityNameCasePair[] = []; - sensitivities.map((sensitivity) => - sensitivity.cases.map((sensitivityCase) => { - availableSensitivityPairs.push({ - sensitivityName: sensitivity.name, - sensitivityCase: sensitivityCase.name, - }); - }) - ); - return availableSensitivityPairs; - }); - - const surfaceMetadataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - - if (!ensembleIdent) { - return null; - } - - return await queryClient.fetchQuery({ - ...getRealizationSurfacesMetadataOptions({ - query: { - case_uuid: ensembleIdent.getCaseUuid(), - ensemble_name: ensembleIdent.getEnsembleName(), - }, - signal: abortSignal, - }), - }); - }); - - availableSettingsUpdater(SettingType.SURFACE_ATTRIBUTE, ({ getHelperDependency }) => { - const data = getHelperDependency(surfaceMetadataDep); - - if (!data) { - return []; - } - - const availableAttributes = [ - ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), - ]; - - return availableAttributes; - }); - availableSettingsUpdater(SettingType.SURFACE_NAME, ({ getHelperDependency, getLocalSetting }) => { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); - const data = getHelperDependency(surfaceMetadataDep); - - if (!attribute || !data) { - return []; - } - - const availableSurfaceNames = [ - ...Array.from( - new Set( - data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name) - ) - ), - ]; - - return availableSurfaceNames; - }); - - availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); - const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); - const data = getHelperDependency(surfaceMetadataDep); - - if (!attribute || !surfaceName || !data) { - return []; - } - - const availableTimeOrIntervals: string[] = []; - const availableTimeTypes = [ - ...Array.from( - new Set( - data.surfaces - .filter((surface) => surface.attribute_name === attribute && surface.name === surfaceName) - .map((el) => el.time_type) - ) - ), - ]; - - if (availableTimeTypes.includes(SurfaceTimeType_api.NO_TIME)) { - availableTimeOrIntervals.push(SurfaceTimeType_api.NO_TIME); - } - if (availableTimeTypes.includes(SurfaceTimeType_api.TIME_POINT)) { - availableTimeOrIntervals.push(...data.time_points_iso_str); - } - if (availableTimeTypes.includes(SurfaceTimeType_api.INTERVAL)) { - availableTimeOrIntervals.push(...data.time_intervals_iso_str); - } - - return availableTimeOrIntervals; - }); - } -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/index.ts deleted file mode 100644 index cf8f62a77..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { StatisticalSurfaceLayer } from "./StatisticalSurfaceLayer"; -export { StatisticalSurfaceSettingsContext } from "./StatisticalSurfaceSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts deleted file mode 100644 index 9df5760de..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { SurfaceStatisticFunction_api } from "@api"; -import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; -import { SensitivityNameCasePair } from "@modules/_shared/LayerFramework/settings/implementations/SensitivitySetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -export type StatisticalSurfaceSettings = { - [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; - [SettingType.STATISTIC_FUNCTION]: SurfaceStatisticFunction_api; - [SettingType.SENSITIVITY]: SensitivityNameCasePair | null; - [SettingType.SURFACE_ATTRIBUTE]: string | null; - [SettingType.SURFACE_NAME]: string | null; - [SettingType.TIME_OR_INTERVAL]: string | null; -}; diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index 2a2935c1a..ddf4085e8 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Icon } from "@equinor/eds-core-react"; -import { color_palette, fault, grid_layer, settings, surface_layer, wellbore } from "@equinor/eds-icons"; +import { color_palette, grid_layer, settings, wellbore } from "@equinor/eds-icons"; import { WorkbenchSession } from "@framework/WorkbenchSession"; import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { Menu } from "@lib/components/Menu"; @@ -10,7 +10,6 @@ import { MenuHeading } from "@lib/components/MenuHeading"; import { MenuItem } from "@lib/components/MenuItem"; import { ObservedSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer"; import { RealizationGridLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer"; -import { RealizationPolygonsLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer"; import { RealizationSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { StatisticalSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; import { PreferredViewLayout } from "@modules/2DViewer/types"; @@ -29,8 +28,6 @@ import { Group, Item, instanceofGroup, instanceofLayer } from "@modules/_shared/ import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; -import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { Dropdown } from "@mui/base"; import { @@ -81,18 +78,6 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper case "color-scale": groupDelegate.prependChild(new ColorScale("Color scale", props.layerManager)); return; - case "observed-surface": - groupDelegate.insertChild(new ObservedSurfaceLayer(props.layerManager), numSharedSettings); - return; - case "statistical-surface": - groupDelegate.insertChild(new StatisticalSurfaceLayer(props.layerManager), numSharedSettings); - return; - case "realization-surface": - groupDelegate.insertChild(new RealizationSurfaceLayer(props.layerManager), numSharedSettings); - return; - case "realization-polygons": - groupDelegate.insertChild(new RealizationPolygonsLayer(props.layerManager), numSharedSettings); - return; case "drilled-wellbore-trajectories": groupDelegate.insertChild(new DrilledWellTrajectoriesLayer(props.layerManager), numSharedSettings); return; @@ -108,12 +93,6 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper case "realization": groupDelegate.prependChild(new SharedSetting(new RealizationSetting(), props.layerManager)); return; - case "surface-name": - groupDelegate.prependChild(new SharedSetting(new SurfaceNameSetting(), props.layerManager)); - return; - case "surface-attribute": - groupDelegate.prependChild(new SharedSetting(new SurfaceAttributeSetting(), props.layerManager)); - return; case "Date": groupDelegate.prependChild(new SharedSetting(new TimeOrIntervalSetting(), props.layerManager)); return; @@ -234,26 +213,6 @@ const LAYER_ACTIONS: LayersActionGroup[] = [ { label: "Layers", children: [ - { - label: "Surfaces", - children: [ - { - identifier: "observed-surface", - icon: , - label: "Observed Surface", - }, - { - identifier: "statistical-surface", - icon: , - label: "Statistical Surface", - }, - { - identifier: "realization-surface", - icon: , - label: "Realization Surface", - }, - ], - }, { label: "Wells", children: [ @@ -269,16 +228,6 @@ const LAYER_ACTIONS: LayersActionGroup[] = [ }, ], }, - { - label: "Polygons", - children: [ - { - identifier: "realization-polygons", - icon: , - label: "Realization Polygons", - }, - ], - }, { label: "Others", children: [ @@ -304,16 +253,6 @@ const LAYER_ACTIONS: LayersActionGroup[] = [ icon: , label: "Realization", }, - { - identifier: "surface-name", - icon: , - label: "Surface Name", - }, - { - identifier: "surface-attribute", - icon: , - label: "Surface Attribute", - }, { identifier: "Date", icon: , diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 5c14031ca..726899bbc 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -5,7 +5,7 @@ import { ViewContext } from "@framework/ModuleContext"; import { useViewStatusWriter } from "@framework/StatusWriter"; import { PendingWrapper } from "@lib/components/PendingWrapper"; import { useElementSize } from "@lib/hooks/useElementSize"; -import { Rect2D, outerRectContainsInnerRect } from "@lib/utils/geometry"; +import { Rect3D, outerRectContainsInnerRect } from "@lib/utils/geometry"; import { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; import { usePublishSubscribeTopicValue } from "@modules/_shared/LayerFramework/delegates/PublishSubscribeDelegate"; @@ -13,8 +13,9 @@ import { LayerManager, LayerManagerTopic } from "@modules/_shared/LayerFramework import { BoundingBox } from "@modules/_shared/LayerFramework/interfaces"; import { ColorLegendsContainer } from "@modules/_shared/components/ColorLegendsContainer"; import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; -import { BoundingBox2D, ViewportType } from "@webviz/subsurface-viewer"; +import { BoundingBox3D, ViewportType } from "@webviz/subsurface-viewer"; import { ViewsType } from "@webviz/subsurface-viewer/dist/SubsurfaceViewer"; +import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; import { ReadoutWrapper } from "./ReadoutWrapper"; @@ -72,7 +73,8 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { id: view.id, name: view.name, isSync: true, - layerIds: [...globalLayerIds, ...view.layers.map((layer) => layer.layer.id), "placeholder"], + show3D: true, + layerIds: [...globalLayerIds, ...view.layers.map((layer) => layer.layer.id), "placeholder", "axes-layer"], }); viewerLayers.push(...view.layers); @@ -100,18 +102,22 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { if (viewsAndLayers.boundingBox !== null) { if (prevBoundingBox !== null) { - const oldBoundingRect: Rect2D | null = { + const oldBoundingRect: Rect3D | null = { x: prevBoundingBox.x[0], y: prevBoundingBox.y[0], + z: prevBoundingBox.z[0], width: prevBoundingBox.x[1] - prevBoundingBox.x[0], height: prevBoundingBox.y[1] - prevBoundingBox.y[0], + depth: prevBoundingBox.z[1] - prevBoundingBox.z[0], }; - const newBoundingRect: Rect2D = { + const newBoundingRect: Rect3D = { x: viewsAndLayers.boundingBox.x[0], y: viewsAndLayers.boundingBox.y[0], + z: viewsAndLayers.boundingBox.z[0], width: viewsAndLayers.boundingBox.x[1] - viewsAndLayers.boundingBox.x[0], height: viewsAndLayers.boundingBox.y[1] - viewsAndLayers.boundingBox.y[0], + depth: viewsAndLayers.boundingBox.z[1] - viewsAndLayers.boundingBox.z[0], }; if (!outerRectContainsInnerRect(oldBoundingRect, newBoundingRect)) { @@ -129,13 +135,21 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { statusWriter.addError(message); } - let bounds: BoundingBox2D | undefined = undefined; + let bounds: BoundingBox3D | undefined = undefined; if (prevBoundingBox) { - bounds = [prevBoundingBox.x[0], prevBoundingBox.y[0], prevBoundingBox.x[1], prevBoundingBox.y[1]]; + bounds = [ + prevBoundingBox.x[0], + prevBoundingBox.y[0], + prevBoundingBox.z[0], + prevBoundingBox.x[1], + prevBoundingBox.y[1], + prevBoundingBox.z[1], + ]; } const layers = viewerLayers.toSorted((a, b) => b.position - a.position).map((layer) => layer.layer); layers.push(new PlaceholderLayer({ id: "placeholder" })); + layers.push(new AxesLayer({ id: "axes-layer", visible: true, ZIncreasingDownwards: true, bounds })); return (
diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index 384b41bc5..123f87fcf 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -2,16 +2,19 @@ import React from "react"; import { Layer as DeckGlLayer } from "@deck.gl/core"; import { SubsurfaceViewerWithCameraState } from "@modules/_shared/components/SubsurfaceViewerWithCameraState"; -import { BoundingBox2D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; +import { BoundingBox3D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; +import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; import { Toolbar } from "./Toolbar"; +import { EditablePolylineLayer } from "../customDeckGlLayers/EditablePolylineLayer"; + export type ReadooutWrapperProps = { views: ViewsType; viewportAnnotations: React.ReactNode[]; layers: DeckGlLayer[]; - bounds?: BoundingBox2D; + bounds?: BoundingBox3D; }; export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { @@ -20,11 +23,21 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const [cameraPositionSetByAction, setCameraPositionSetByAction] = React.useState(null); const [triggerHomeCounter, setTriggerHomeCounter] = React.useState(0); const [layerPickingInfo, setLayerPickingInfo] = React.useState([]); + const [gridVisible, setGridVisible] = React.useState(false); + const [verticalScale, setVerticalScale] = React.useState(1); function handleFitInViewClick() { setTriggerHomeCounter((prev) => prev + 1); } + function handleGridVisibilityChange(visible: boolean) { + setGridVisible(visible); + } + + function handleEditPolylines() { + console.log("Edit polylines"); + } + function handleMouseHover(event: MapMouseEvent): void { setLayerPickingInfo(event.infos); } @@ -35,18 +48,34 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { } } + function handleVerticalScaleChange(value: number) { + setVerticalScale(value); + } + + let adjustedLayers = [...props.layers]; + if (!gridVisible) { + adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); + } + adjustedLayers.push(new EditablePolylineLayer({ id: "editable-polyline", editable: true, polylines: [] })); + return ( <> - + setCameraPositionSetByAction(null)} onMouseEvent={handleMouseEvent} - layers={props.layers} + layers={adjustedLayers} + verticalScale={verticalScale} scale={{ visible: true, incrementValue: 100, diff --git a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx index 08527e27d..2dc9eea1c 100644 --- a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx @@ -1,21 +1,67 @@ +import React from "react"; + import { Button } from "@lib/components/Button"; -import { Toolbar as GenericToolbar } from "@modules/_shared/components/Toolbar"; -import { FilterCenterFocus } from "@mui/icons-material"; +import { HoldPressedIntervalCallbackButton } from "@lib/components/HoldPressedIntervalCallbackButton/holdPressedIntervalCallbackButton"; +import { ToggleButton } from "@lib/components/ToggleButton"; +import { Toolbar as GenericToolbar, ToolBarDivider } from "@modules/_shared/components/Toolbar"; +import { Add, FilterCenterFocus, GridOff, GridOn, Polyline, Remove } from "@mui/icons-material"; export type ToolbarProps = { + verticalScale: number; onFitInView: () => void; + onGridVisibilityChange: (visible: boolean) => void; + onEditPolyline: () => void; + onVerticalScaleChange(value: number): void; }; export function Toolbar(props: ToolbarProps): React.ReactNode { + const [gridVisible, setGridVisible] = React.useState(false); + function handleFitInViewClick() { props.onFitInView(); } + function handleGridToggle() { + props.onGridVisibilityChange(!gridVisible); + setGridVisible(!gridVisible); + } + + function handleVerticalScaleIncrease() { + props.onVerticalScaleChange(props.verticalScale + 0.1); + } + + function handleVerticalScaleDecrease() { + props.onVerticalScaleChange(props.verticalScale - 0.1); + } + return ( + + {gridVisible ? : } + + + + + + + + + {props.verticalScale.toFixed(2)} + + + + ); } diff --git a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/EditablePolylineLayer.ts b/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/EditablePolylineLayer.ts new file mode 100644 index 000000000..dd272b36b --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/EditablePolylineLayer.ts @@ -0,0 +1,264 @@ +import { CompositeLayer, Layer, PickingInfo } from "@deck.gl/core"; +import { ColumnLayer, SolidPolygonLayer } from "@deck.gl/layers"; + +export type EditablePolylineLayerProps = { + id: string; + editable: boolean; + polylines: Polyline[]; +}; + +export type Polyline = { + id: string; + color: [number, number, number, number]; + polyline: number[][]; +}; + +export class EditablePolylineLayer extends CompositeLayer { + static layerName: string = "EditablePolylineLayer"; + + private _polylines: Polyline[] = []; + private _editingPolylineId: string | null = null; + private _hoveredPolylinePointIndex: number | null = null; + private _hoveredPreviewPoint: number[] | null = null; + private _isDragging = false; + + makePolylineData( + polyline: number[][], + zMid: number, + zExtension: number, + selectedPolylineIndex: number | null, + hoveredPolylineIndex: number | null, + color: [number, number, number, number] + ): { + polygonData: { polygon: number[][]; color: number[] }[]; + columnData: { index: number; centroid: number[]; color: number[] }[]; + } { + const polygonData: { + polygon: number[][]; + color: number[]; + }[] = []; + + const columnData: { + index: number; + centroid: number[]; + color: number[]; + }[] = []; + + const width = 10; + for (let i = 0; i < polyline.length; i++) { + const startPoint = polyline[i]; + const endPoint = polyline[i + 1]; + + if (i < polyline.length - 1) { + const lineVector = [endPoint[0] - startPoint[0], endPoint[1] - startPoint[1], 0]; + const zVector = [0, 0, 1]; + const normalVector = [ + lineVector[1] * zVector[2] - lineVector[2] * zVector[1], + lineVector[2] * zVector[0] - lineVector[0] * zVector[2], + lineVector[0] * zVector[1] - lineVector[1] * zVector[0], + ]; + const normalizedNormalVector = [ + normalVector[0] / Math.sqrt(normalVector[0] ** 2 + normalVector[1] ** 2 + normalVector[2] ** 2), + normalVector[1] / Math.sqrt(normalVector[0] ** 2 + normalVector[1] ** 2 + normalVector[2] ** 2), + ]; + + const point1 = [ + startPoint[0] - (normalizedNormalVector[0] * width) / 2, + startPoint[1] - (normalizedNormalVector[1] * width) / 2, + zMid - zExtension / 2, + ]; + + const point2 = [ + endPoint[0] - (normalizedNormalVector[0] * width) / 2, + endPoint[1] - (normalizedNormalVector[1] * width) / 2, + zMid - zExtension / 2, + ]; + + const point3 = [ + endPoint[0] + (normalizedNormalVector[0] * width) / 2, + endPoint[1] + (normalizedNormalVector[1] * width) / 2, + zMid - zExtension / 2, + ]; + + const point4 = [ + startPoint[0] + (normalizedNormalVector[0] * width) / 2, + startPoint[1] + (normalizedNormalVector[1] * width) / 2, + zMid - zExtension / 2, + ]; + + const polygon: number[][] = [point1, point2, point3, point4]; + polygonData.push({ polygon, color: [color[0], color[1], color[2], color[3] / 2] }); + } + + let adjustedColor = color; + if (i === selectedPolylineIndex) { + if (i === 0 || i === polyline.length - 1) { + adjustedColor = [0, 255, 0, color[3]]; + if (i === hoveredPolylineIndex) { + adjustedColor = [200, 255, 200, color[3]]; + } + } else { + adjustedColor = [60, 60, 255, color[3]]; + if (i === hoveredPolylineIndex) { + adjustedColor = [120, 120, 255, color[3]]; + } + } + } else if (i === hoveredPolylineIndex) { + adjustedColor = [120, 120, 255, color[3]]; + } + columnData.push({ + index: i, + centroid: [startPoint[0], startPoint[1], zMid - zExtension / 2], + color: adjustedColor, + }); + } + + return { polygonData, columnData }; + } + + private getPolylineById(id: string): Polyline | undefined { + return this._polylines.find((polyline) => polyline.id === id); + } + + handlePolylineHover(pickingInfo: PickingInfo): void { + if (!this._editingPolylineId) { + return; + } + const polyline = this.getPolylineById(this._editingPolylineId); + if (!polyline) { + return; + } + if (pickingInfo.object && pickingInfo.object.index < polyline.polyline.length) { + this._hoveredPolylinePointIndex = pickingInfo.object.index; + } else { + this._hoveredPolylinePointIndex = null; + } + + this.setNeedsUpdate(); + } + + handlePolylineClick(pickingInfo: PickingInfo, event: any): void { + if (!this._editingPolylineId) { + return; + } + const polyline = this.getPolylineById(this._editingPolylineId); + if (!polyline) { + return; + } + + if (pickingInfo.object && pickingInfo.object.index < polyline.polyline.length) { + this._hoveredPreviewPoint = null; + this._hoveredPolylinePointIndex = pickingInfo.object.index; + event.stopPropagation(); + event.handled = true; + } else { + this._hoveredPolylinePointIndex = null; + } + } + + handlePolylineDragStart(): void { + this._hoveredPreviewPoint = null; + this._isDragging = true; + + if (!this._editingPolylineId) { + return; + } + } + + handlePolylineDragEnd(): void { + this._isDragging = false; + } + + handlePolylineDrag(pickingInfo: PickingInfo): void { + if (!this._editingPolylineId) { + return; + } + + if (pickingInfo.object) { + const index = pickingInfo.object.index; + if (!pickingInfo.coordinate) { + return; + } + + const polyline = this.getPolylineById(this._editingPolylineId); + if (!polyline) { + return; + } + + const newPolyline = polyline.polyline.reduce((acc, point, i) => { + if (i === index && pickingInfo.coordinate) { + return [...acc, [pickingInfo.coordinate[0], pickingInfo.coordinate[1]]]; + } + return [...acc, point]; + }, [] as number[][]); + + polyline.polyline = newPolyline; + } + } + + onHover(event: any): boolean { + this._hoveredPreviewPoint = event.coordinate; + this.setNeedsUpdate(); + return true; + } + + renderLayers() { + const layers: Layer[] = []; + + const previewData: { centroid: number[]; color: [number, number, number, number] }[] = []; + if (this._hoveredPreviewPoint) { + previewData.push({ + centroid: this._hoveredPreviewPoint, + color: [255, 255, 255, 100], + }); + } + + for (const polyline of this._polylines) { + const { polygonData, columnData } = this.makePolylineData(polyline.polyline, 0, 10, 0, 0, polyline.color); + + layers.push( + new SolidPolygonLayer({ + id: `polygon-${polyline.id}`, + data: polygonData, + getPolygon: (d) => d.polygon, + getFillColor: (d) => d.color, + extruded: true, + wireframe: true, + opacity: 0.5, + }), + new ColumnLayer({ + id: "user-polyline-point-layer", + data: columnData, + getElevation: 1, + getPosition: (d) => d.centroid, + getFillColor: (d) => d.color, + extruded: true, + radius: 50, + radiusUnits: "pixels", + pickable: true, + onHover: this.handlePolylineHover, + onClick: this.handlePolylineClick, + onDragStart: this.handlePolylineDragStart, + onDragEnd: this.handlePolylineDragEnd, + onDrag: this.handlePolylineDrag, + }) + ); + } + + layers.push( + new ColumnLayer({ + id: "user-polyline-hover-point-layer", + data: previewData, + getElevation: 1000, + getPosition: (d) => d.centroid, + getFillColor: (d) => d.color, + extruded: true, + radius: 50, + radiusUnits: "pixels", + pickable: true, + }) + ); + + return layers; + } +} diff --git a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts index 7e7f2329c..09b0ffa3e 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts @@ -1,24 +1,18 @@ -import { PolygonData_api, SurfaceDataPng_api, SurfaceDef_api, WellborePick_api, WellboreTrajectory_api } from "@api"; +import { WellborePick_api, WellboreTrajectory_api } from "@api"; import { Layer } from "@deck.gl/core"; -import { GeoJsonLayer } from "@deck.gl/layers"; import { defaultColorPalettes } from "@framework/utils/colorPalettes"; import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; -import { Vec2, rotatePoint2Around } from "@lib/utils/vec2"; import { GridMappedProperty_trans, GridSurface_trans } from "@modules/3DViewer/view/queries/queryDataTransforms"; import { Layer as LayerInterface } from "@modules/_shared/LayerFramework/interfaces"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; -import { ColormapLayer, Grid3DLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; +import { Grid3DLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; import { Rgb, parse } from "culori"; -import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "geojson"; +import { Feature } from "geojson"; -import { ObservedSurfaceLayer } from "../../LayerFramework/customLayerImplementations/ObservedSurfaceLayer"; import { RealizationGridLayer } from "../../LayerFramework/customLayerImplementations/RealizationGridLayer"; -import { RealizationPolygonsLayer } from "../../LayerFramework/customLayerImplementations/RealizationPolygonsLayer"; -import { RealizationSurfaceLayer } from "../../LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; -import { StatisticalSurfaceLayer } from "../../LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; import { AdvancedWellsLayer } from "../customDeckGlLayers/AdvancedWellsLayer"; import { WellBorePickLayerData, WellborePicksLayer } from "../customDeckGlLayers/WellborePicksLayer"; @@ -38,33 +32,6 @@ export function makeDeckGlLayer(layer: LayerInterface, colorScale?: Co if (!data) { return null; } - if (layer instanceof ObservedSurfaceLayer) { - return createMapImageLayer( - data, - layer.getItemDelegate().getId(), - layer.getItemDelegate().getName(), - colorScale - ); - } - if (layer instanceof RealizationSurfaceLayer) { - return createMapImageLayer( - data, - layer.getItemDelegate().getId(), - layer.getItemDelegate().getName(), - colorScale - ); - } - if (layer instanceof StatisticalSurfaceLayer) { - return createMapImageLayer( - data, - layer.getItemDelegate().getId(), - layer.getItemDelegate().getName(), - colorScale - ); - } - if (layer instanceof RealizationPolygonsLayer) { - return createPolygonsLayer(data, layer.getItemDelegate().getId()); - } if (layer instanceof DrilledWellTrajectoriesLayer) { return makeWellsLayer(data, layer.getItemDelegate().getId(), null); } @@ -101,77 +68,6 @@ function createWellPicksLayer(wellPicksDataApi: WellborePick_api[], id: string): }); } -function createMapImageLayer( - layerData: SurfaceDataPng_api, - id: string, - name: string, - colorScale?: ColorScaleWithName -): ColormapLayer { - return new ColormapLayer({ - id: id, - name: name, - image: `data:image/png;base64,${layerData.png_image_base64}`, - bounds: calcBoundsForRotationAroundUpperLeftCorner(layerData.surface_def), - rotDeg: layerData.surface_def.rot_deg, - valueRange: [layerData.value_min, layerData.value_max], - colorMapRange: [layerData.value_min, layerData.value_max], - colorMapName: "Physics", - parameters: { - depthWriteEnabled: false, - }, - colorMapFunction: makeColorMapFunction(colorScale, layerData.value_min, layerData.value_max), - }); -} - -function calcBoundsForRotationAroundUpperLeftCorner(surfDef: SurfaceDef_api): [number, number, number, number] { - const width = (surfDef.npoints_x - 1) * surfDef.inc_x; - const height = (surfDef.npoints_y - 1) * surfDef.inc_y; - const orgRotPoint: Vec2 = { x: surfDef.origin_utm_x, y: surfDef.origin_utm_y }; - const orgTopLeft: Vec2 = { x: surfDef.origin_utm_x, y: surfDef.origin_utm_y + height }; - - const transTopLeft: Vec2 = rotatePoint2Around(orgTopLeft, orgRotPoint, (surfDef.rot_deg * Math.PI) / 180); - const tLeft = transTopLeft.x; - const tBottom = transTopLeft.y - height; - const tRight = transTopLeft.x + width; - const tTop = transTopLeft.y; - - const bounds: [number, number, number, number] = [tLeft, tBottom, tRight, tTop]; - - return bounds; -} - -function createPolygonsLayer(polygonsData: PolygonData_api[], id: string): GeoJsonLayer { - const features: Feature[] = polygonsData.map((polygon) => { - return polygonsToGeojson(polygon); - }); - const data: FeatureCollection = { - type: "FeatureCollection", - features: features, - }; - return new GeoJsonLayer({ - id: id, - data: data, - filled: false, - lineWidthMinPixels: 2, - parameters: { - depthTest: false, - }, - - pickable: true, - }); -} -function polygonsToGeojson(polygons: PolygonData_api): Feature { - const data: Feature = { - type: "Feature", - geometry: { - type: "Polygon", - coordinates: [zipCoords(polygons.x_arr, polygons.y_arr, polygons.z_arr)], - }, - properties: { name: polygons.poly_id, color: [0, 0, 0, 255] }, - }; - return data; -} - function makeWellsLayer( fieldWellboreTrajectoriesData: WellboreTrajectory_api[], id: string, diff --git a/frontend/src/modules/_shared/components/Toolbar/toolbar.tsx b/frontend/src/modules/_shared/components/Toolbar/toolbar.tsx index 3ca04a388..79786d043 100644 --- a/frontend/src/modules/_shared/components/Toolbar/toolbar.tsx +++ b/frontend/src/modules/_shared/components/Toolbar/toolbar.tsx @@ -9,7 +9,7 @@ export function Toolbar(props: ToolbarProps): React.ReactNode { } return ( -
+
{props.children}
); diff --git a/frontend/src/modules/_shared/components/Toolbar/toolbarDivider.tsx b/frontend/src/modules/_shared/components/Toolbar/toolbarDivider.tsx index c3c141c7f..e1e995713 100644 --- a/frontend/src/modules/_shared/components/Toolbar/toolbarDivider.tsx +++ b/frontend/src/modules/_shared/components/Toolbar/toolbarDivider.tsx @@ -1,3 +1,3 @@ export function ToolBarDivider(): React.ReactNode { - return
; + return
; } From d0700c2283d827f79a6c34267f2ccb92c472b506 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 16 Jan 2025 09:30:47 +0100 Subject: [PATCH 03/97] wip --- .../editablePolylinesHook.ts | 26 +++++++++++++++++++ .../view/hooks/editablePolylines/types.ts | 5 ++++ 2 files changed, 31 insertions(+) create mode 100644 frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts create mode 100644 frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts new file mode 100644 index 000000000..a40846f62 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts @@ -0,0 +1,26 @@ +import React from "react"; + +import { Layer } from "@deck.gl/core"; +import { MapMouseEvent } from "@webviz/subsurface-viewer"; + +import { Polyline } from "./types"; + +export type UseEditablePolylinesProps = { + polylines: Polyline[]; +}; + +export type UseEditablePolylinesReturnType = { + layers: Layer[]; + onMouseEvent: (event: MapMouseEvent) => void; +}; + +export function useEditablePolylines(props: UseEditablePolylinesProps): UseEditablePolylinesReturnType { + const onMouseEvent = React.useCallback((event: MapMouseEvent) => { + console.log("MouseEvent", event); + }, []); + + return { + layers: [], + onMouseEvent, + }; +} diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts new file mode 100644 index 000000000..8309ac056 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts @@ -0,0 +1,5 @@ +export type Polyline = { + id: string; + color: [number, number, number, number]; + polyline: number[][]; +}; From 73990bd7836d624aa62bd6674e206b3d0be9429d Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 16 Jan 2025 13:30:30 +0100 Subject: [PATCH 04/97] wip --- .../view/components/LayersWrapper.tsx | 9 +- .../view/components/ReadoutWrapper.tsx | 12 ++- .../deckGlLayers}/EditablePolylineLayer.ts | 69 +++++--------- .../deckGlLayers/HoverPointLayer.ts | 30 ++++++ .../editablePolylinesHook.ts | 91 ++++++++++++++++++- 5 files changed, 155 insertions(+), 56 deletions(-) rename frontend/src/modules/3DViewerNew/view/{customDeckGlLayers => hooks/editablePolylines/deckGlLayers}/EditablePolylineLayer.ts (80%) create mode 100644 frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/HoverPointLayer.ts diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 726899bbc..3839b4304 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -74,7 +74,14 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { name: view.name, isSync: true, show3D: true, - layerIds: [...globalLayerIds, ...view.layers.map((layer) => layer.layer.id), "placeholder", "axes-layer"], + layerIds: [ + ...globalLayerIds, + ...view.layers.map((layer) => layer.layer.id), + "placeholder", + "axes-layer", + "editable-polylines-layer", + "hover-point-layer", + ], }); viewerLayers.push(...view.layers); diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index 123f87fcf..aa9ef5ce6 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -8,7 +8,7 @@ import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; import { Toolbar } from "./Toolbar"; -import { EditablePolylineLayer } from "../customDeckGlLayers/EditablePolylineLayer"; +import { useEditablePolylines } from "../hooks/editablePolylines/editablePolylinesHook"; export type ReadooutWrapperProps = { views: ViewsType; @@ -25,6 +25,9 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const [layerPickingInfo, setLayerPickingInfo] = React.useState([]); const [gridVisible, setGridVisible] = React.useState(false); const [verticalScale, setVerticalScale] = React.useState(1); + const [polylineEditingActive, setPolylineEditingActive] = React.useState(false); + + const { onMouseEvent, layers } = useEditablePolylines({ polylines: [], editingActive: polylineEditingActive }); function handleFitInViewClick() { setTriggerHomeCounter((prev) => prev + 1); @@ -35,7 +38,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { } function handleEditPolylines() { - console.log("Edit polylines"); + setPolylineEditingActive((prev) => !prev); } function handleMouseHover(event: MapMouseEvent): void { @@ -46,6 +49,8 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { if (event.type === "hover") { handleMouseHover(event); } + + onMouseEvent(event); } function handleVerticalScaleChange(value: number) { @@ -56,7 +61,8 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { if (!gridVisible) { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); } - adjustedLayers.push(new EditablePolylineLayer({ id: "editable-polyline", editable: true, polylines: [] })); + + adjustedLayers.push(...layers); return ( <> diff --git a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/EditablePolylineLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts similarity index 80% rename from frontend/src/modules/3DViewerNew/view/customDeckGlLayers/EditablePolylineLayer.ts rename to frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts index dd272b36b..35fbc791b 100644 --- a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/EditablePolylineLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts @@ -1,5 +1,5 @@ import { CompositeLayer, Layer, PickingInfo } from "@deck.gl/core"; -import { ColumnLayer, SolidPolygonLayer } from "@deck.gl/layers"; +import { ColumnLayer, PathLayer } from "@deck.gl/layers"; export type EditablePolylineLayerProps = { id: string; @@ -19,7 +19,6 @@ export class EditablePolylineLayer extends CompositeLayer[] = []; - const previewData: { centroid: number[]; color: [number, number, number, number] }[] = []; - if (this._hoveredPreviewPoint) { - previewData.push({ - centroid: this._hoveredPreviewPoint, - color: [255, 255, 255, 100], - }); - } - - for (const polyline of this._polylines) { - const { polygonData, columnData } = this.makePolylineData(polyline.polyline, 0, 10, 0, 0, polyline.color); - + for (const polyline of this.props.polylines) { layers.push( - new SolidPolygonLayer({ - id: `polygon-${polyline.id}`, - data: polygonData, - getPolygon: (d) => d.polygon, - getFillColor: (d) => d.color, - extruded: true, - wireframe: true, - opacity: 0.5, + new PathLayer({ + id: `lines-${polyline.id}`, + data: polyline.polyline, + getPath: (d) => [d[0], d[1], 0], + getWidth: 3, + getColor: polyline.color, + widthUnits: "pixels", + parameters: { + depthTest: false, + }, + billboard: true, }), new ColumnLayer({ - id: "user-polyline-point-layer", - data: columnData, + id: `points-${polyline.id}`, + data: polyline.polyline, getElevation: 1, - getPosition: (d) => d.centroid, - getFillColor: (d) => d.color, - extruded: true, + getPosition: (d) => d, + getFillColor: polyline.color, + extruded: false, radius: 50, radiusUnits: "pixels", pickable: true, @@ -241,24 +225,13 @@ export class EditablePolylineLayer extends CompositeLayer d.centroid, - getFillColor: (d) => d.color, - extruded: true, - radius: 50, - radiusUnits: "pixels", - pickable: true, - }) - ); - return layers; } } diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/HoverPointLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/HoverPointLayer.ts new file mode 100644 index 000000000..7ca68906c --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/HoverPointLayer.ts @@ -0,0 +1,30 @@ +import { CompositeLayer, Layer, LayersList } from "@deck.gl/core"; +import { ColumnLayer } from "@deck.gl/layers"; + +export type HoverPointLayerProps = { + point: number[] | null; + color: [number, number, number, number]; +}; + +export class HoverPointLayer extends CompositeLayer { + static layerName: string = "HoverPointLayer"; + + renderLayers(): Layer | null | LayersList { + if (!this.props.point) { + return null; + } + + return new ColumnLayer({ + id: "hover-point", + data: [this.props.point], + diskResolution: 20, + getElevation: 1, + radiusUnits: "pixels", + radius: 20, + extruded: false, + pickable: false, + getPosition: (d) => d, + getFillColor: this.props.color, + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts index a40846f62..25bd98fd8 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts @@ -3,9 +3,15 @@ import React from "react"; import { Layer } from "@deck.gl/core"; import { MapMouseEvent } from "@webviz/subsurface-viewer"; +import { isEqual } from "lodash"; +import { v4 } from "uuid"; + +import { EditablePolylineLayer } from "./deckGlLayers/EditablePolylineLayer"; +import { HoverPointLayer } from "./deckGlLayers/HoverPointLayer"; import { Polyline } from "./types"; export type UseEditablePolylinesProps = { + editingActive: boolean; polylines: Polyline[]; }; @@ -15,12 +21,89 @@ export type UseEditablePolylinesReturnType = { }; export function useEditablePolylines(props: UseEditablePolylinesProps): UseEditablePolylinesReturnType { - const onMouseEvent = React.useCallback((event: MapMouseEvent) => { - console.log("MouseEvent", event); - }, []); + const [hoverPoint, setHoverPoint] = React.useState(null); + const [activePolylineId, setActivePolylineId] = React.useState(null); + const [polylines, setPolylines] = React.useState(props.polylines); + const [prevPolylines, setPrevPolylines] = React.useState(props.polylines); + + if (!isEqual(props.polylines, prevPolylines)) { + setPolylines(props.polylines); + setPrevPolylines(props.polylines); + } + + const onMouseEvent = React.useCallback( + (event: MapMouseEvent) => { + if (event.type === "hover") { + if (!props.editingActive) { + setHoverPoint(null); + return; + } + if (!event.x || !event.y) { + setHoverPoint(null); + return; + } + const firstLayerInfos = event.infos[0]; + if (firstLayerInfos && firstLayerInfos.coordinate) { + setHoverPoint([...firstLayerInfos.coordinate]); + } + } + + if (event.type === "click") { + if (!props.editingActive) { + return; + } + + const firstLayerInfos = event.infos[0]; + if (!firstLayerInfos || !firstLayerInfos.coordinate) { + return; + } + + if (!activePolylineId) { + const uuid = v4(); + setActivePolylineId(uuid); + setPolylines([ + ...polylines, + { id: uuid, color: [0, 0, 255, 255], polyline: [[...firstLayerInfos.coordinate]] }, + ]); + } else { + const updatedPolylines = polylines.map((polyline) => { + if (polyline.id === activePolylineId) { + if (!event.x || !event.y) { + return polyline; + } + const firstLayerInfos = event.infos[0]; + if (!firstLayerInfos || !firstLayerInfos.coordinate) { + return polyline; + } + return { + ...polyline, + polyline: [...polyline.polyline, [...firstLayerInfos.coordinate]], + }; + } + return polyline; + }); + setPolylines(updatedPolylines); + } + } + }, + [props.editingActive, activePolylineId, polylines] + ); + + const layers = [ + new HoverPointLayer({ + id: "hover-point-layer", + point: hoverPoint, + color: [255, 0, 0, 100], + }), + new EditablePolylineLayer({ + id: "editable-polylines-layer", + polylines, + editable: props.editingActive, + }), + ]; return { - layers: [], + layers, onMouseEvent, }; } From 1230d86235159424ff0303c3b91a789540668ba2 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 16 Jan 2025 15:21:53 +0100 Subject: [PATCH 05/97] wip --- .../deckGlLayers/EditablePolylineLayer.ts | 111 +++++------------- .../deckGlLayers/HoverPointLayer.ts | 3 + .../editablePolylinesHook.ts | 6 +- 3 files changed, 33 insertions(+), 87 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts index 35fbc791b..6aa11e5f2 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts @@ -1,9 +1,9 @@ -import { CompositeLayer, Layer, PickingInfo } from "@deck.gl/core"; -import { ColumnLayer, PathLayer } from "@deck.gl/layers"; +import { CompositeLayer, GetPickingInfoParams, Layer, PickingInfo } from "@deck.gl/core"; +import { ColumnLayer, LineLayer } from "@deck.gl/layers"; export type EditablePolylineLayerProps = { id: string; - editable: boolean; + editablePolylineId: string | null; polylines: Polyline[]; }; @@ -16,10 +16,8 @@ export type Polyline = { export class EditablePolylineLayer extends CompositeLayer { static layerName: string = "EditablePolylineLayer"; - private _polylines: Polyline[] = []; private _editingPolylineId: string | null = null; private _hoveredPolylinePointIndex: number | null = null; - private _isDragging = false; makePolylineData( polyline: number[][], @@ -116,115 +114,60 @@ export class EditablePolylineLayer extends CompositeLayer polyline.id === id); + return this.props.polylines.find((polyline) => polyline.id === id); } - handlePolylineHover(pickingInfo: PickingInfo): void { + getPickingInfo({ info }: GetPickingInfoParams): PickingInfo { if (!this._editingPolylineId) { - return; + return info; } const polyline = this.getPolylineById(this._editingPolylineId); if (!polyline) { - return; + return info; } - if (pickingInfo.object && pickingInfo.object.index < polyline.polyline.length) { - this._hoveredPolylinePointIndex = pickingInfo.object.index; + if (info.object && info.object.index < polyline.polyline.length) { + this._hoveredPolylinePointIndex = info.object.index; } else { this._hoveredPolylinePointIndex = null; } this.setNeedsUpdate(); - } - - handlePolylineClick(pickingInfo: PickingInfo, event: any): void { - if (!this._editingPolylineId) { - return; - } - const polyline = this.getPolylineById(this._editingPolylineId); - if (!polyline) { - return; - } - - if (pickingInfo.object && pickingInfo.object.index < polyline.polyline.length) { - this._hoveredPolylinePointIndex = pickingInfo.object.index; - event.stopPropagation(); - event.handled = true; - } else { - this._hoveredPolylinePointIndex = null; - } - } - - handlePolylineDragStart(): void { - this._isDragging = true; - - if (!this._editingPolylineId) { - return; - } - } - - handlePolylineDragEnd(): void { - this._isDragging = false; - } - - handlePolylineDrag(pickingInfo: PickingInfo): void { - if (!this._editingPolylineId) { - return; - } - - if (pickingInfo.object) { - const index = pickingInfo.object.index; - if (!pickingInfo.coordinate) { - return; - } - - const polyline = this.getPolylineById(this._editingPolylineId); - if (!polyline) { - return; - } - - const newPolyline = polyline.polyline.reduce((acc, point, i) => { - if (i === index && pickingInfo.coordinate) { - return [...acc, [pickingInfo.coordinate[0], pickingInfo.coordinate[1]]]; - } - return [...acc, point]; - }, [] as number[][]); - - polyline.polyline = newPolyline; - } + return info; } renderLayers() { const layers: Layer[] = []; for (const polyline of this.props.polylines) { + const polylineData: { from: number[]; to: number[] }[] = []; + for (let i = 0; i < polyline.polyline.length - 1; i++) { + polylineData.push({ from: polyline.polyline[i], to: polyline.polyline[i + 1] }); + } layers.push( - new PathLayer({ + new LineLayer({ id: `lines-${polyline.id}`, - data: polyline.polyline, - getPath: (d) => [d[0], d[1], 0], - getWidth: 3, + data: polylineData, getColor: polyline.color, - widthUnits: "pixels", - parameters: { - depthTest: false, - }, + getSourcePosition: (d) => d.from, + getTargetPosition: (d) => d.to, + getWidth: 3, + parameters: { depthTest: false }, billboard: true, - }), + widthUnits: "pixels", + }) + ); + layers.push( new ColumnLayer({ id: `points-${polyline.id}`, data: polyline.polyline, getElevation: 1, getPosition: (d) => d, - getFillColor: polyline.color, + getFillColor: (d, i) => + this._hoveredPolylinePointIndex === i.index ? [0, 0, 255, 255] : polyline.color, extruded: false, - radius: 50, + radius: 20, radiusUnits: "pixels", pickable: true, - onHover: this.handlePolylineHover, - onClick: this.handlePolylineClick, - onDragStart: this.handlePolylineDragStart, - onDragEnd: this.handlePolylineDragEnd, - onDrag: this.handlePolylineDrag, parameters: { depthTest: false, }, diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/HoverPointLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/HoverPointLayer.ts index 7ca68906c..8bc04b19c 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/HoverPointLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/HoverPointLayer.ts @@ -25,6 +25,9 @@ export class HoverPointLayer extends CompositeLayer { pickable: false, getPosition: (d) => d, getFillColor: this.props.color, + parameters: { + depthTest: false, + }, }); } } diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts index 25bd98fd8..1c430d979 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts @@ -63,7 +63,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita setActivePolylineId(uuid); setPolylines([ ...polylines, - { id: uuid, color: [0, 0, 255, 255], polyline: [[...firstLayerInfos.coordinate]] }, + { id: uuid, color: [255, 255, 255, 255], polyline: [[...firstLayerInfos.coordinate]] }, ]); } else { const updatedPolylines = polylines.map((polyline) => { @@ -93,12 +93,12 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita new HoverPointLayer({ id: "hover-point-layer", point: hoverPoint, - color: [255, 0, 0, 100], + color: [255, 255, 255, 100], }), new EditablePolylineLayer({ id: "editable-polylines-layer", polylines, - editable: props.editingActive, + editablePolylineId: activePolylineId, }), ]; From bce97a86a710234ca8605e5add24cc3c30473d28 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 16 Jan 2025 17:59:44 +0100 Subject: [PATCH 06/97] wip --- .../deckGlLayers/AnimatedPathLayer.ts | 39 ++++++++ .../deckGlLayers/EditablePolylineLayer.ts | 99 ++++++++++++++----- .../editablePolylinesHook.ts | 4 +- 3 files changed, 115 insertions(+), 27 deletions(-) create mode 100644 frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/AnimatedPathLayer.ts diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/AnimatedPathLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/AnimatedPathLayer.ts new file mode 100644 index 000000000..ed207243c --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/AnimatedPathLayer.ts @@ -0,0 +1,39 @@ +import { UpdateParameters } from "@deck.gl/core"; +import { PathLayer } from "@deck.gl/layers"; + +export class AnimatedPathLayer extends PathLayer { + static layerName = "AnimatedPathLayer"; + + private _dashStart: number = 0; + + initializeState() { + super.initializeState(); + this.animate(); + } + + updateState(params: UpdateParameters): void { + super.updateState(params); + this.animate(); + } + + private animate() { + this._dashStart = (Date.now() / 50) % 1000; + + this.setNeedsRedraw(); + requestAnimationFrame(() => this.animate()); + } + + getShaders() { + const shaders = super.getShaders(); + shaders.inject["vs:#decl"] += `\ + uniform float dashStart;`; + shaders.inject["vs:#main-end"] += `\ + vDashOffset += dashStart;`; + return shaders; + } + + draw({ uniforms }: Record) { + uniforms.dashStart = this._dashStart || 0; + super.draw({ uniforms }); + } +} diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts index 6aa11e5f2..c7b39c9ff 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts @@ -1,5 +1,8 @@ -import { CompositeLayer, GetPickingInfoParams, Layer, PickingInfo } from "@deck.gl/core"; -import { ColumnLayer, LineLayer } from "@deck.gl/layers"; +import { CompositeLayer, Layer, LayerContext, PickingInfo } from "@deck.gl/core"; +import { PathStyleExtension } from "@deck.gl/extensions"; +import { ColumnLayer } from "@deck.gl/layers"; + +import { AnimatedPathLayer } from "./AnimatedPathLayer"; export type EditablePolylineLayerProps = { id: string; @@ -16,8 +19,21 @@ export type Polyline = { export class EditablePolylineLayer extends CompositeLayer { static layerName: string = "EditablePolylineLayer"; - private _editingPolylineId: string | null = null; - private _hoveredPolylinePointIndex: number | null = null; + // @ts-expect-error + state!: { + hoveredPolylinePoint: { + polylineId: string; + pointIndex: number; + } | null; + dashStart: number; + }; + + initializeState(context: LayerContext): void { + this.state = { + hoveredPolylinePoint: null, + dashStart: 0, + }; + } makePolylineData( polyline: number[][], @@ -117,22 +133,43 @@ export class EditablePolylineLayer extends CompositeLayer polyline.id === id); } - getPickingInfo({ info }: GetPickingInfoParams): PickingInfo { - if (!this._editingPolylineId) { - return info; - } - const polyline = this.getPolylineById(this._editingPolylineId); - if (!polyline) { - return info; + private extractPolylineIdFromSourceLayerId(sourceLayerId: string): string | null { + const match = sourceLayerId.match(/points-(.*)/); + if (!match) { + return null; } - if (info.object && info.object.index < polyline.polyline.length) { - this._hoveredPolylinePointIndex = info.object.index; - } else { - this._hoveredPolylinePointIndex = null; + + return match[1]; + } + + onHover(info: PickingInfo): boolean { + const sourceLayerId = info.sourceLayer?.id; + if (info.index !== undefined && sourceLayerId) { + const polylineId = this.extractPolylineIdFromSourceLayerId(sourceLayerId); + if (!polylineId) { + return false; + } + + const polyline = this.getPolylineById(polylineId); + if (!polyline) { + return false; + } + + this.setState({ + hoveredPolylinePoint: { + polylineId, + pointIndex: info.index, + }, + }); + + return true; } - this.setNeedsUpdate(); - return info; + this.setState({ + hoveredPolylinePoint: null, + }); + + return false; } renderLayers() { @@ -144,16 +181,22 @@ export class EditablePolylineLayer extends CompositeLayer d.from, - getTargetPosition: (d) => d.to, - getWidth: 3, - parameters: { depthTest: false }, + getPath: (d) => d, + getDashArray: [10, 10], + getWidth: 10, billboard: true, - widthUnits: "pixels", + widthUnits: "meters", + extensions: [new PathStyleExtension({ highPrecisionDash: true })], + parameters: { + // @ts-expect-error - deck.gl types are wrong + depthTest: false, + }, + depthTest: false, }) ); layers.push( @@ -163,7 +206,10 @@ export class EditablePolylineLayer extends CompositeLayer d, getFillColor: (d, i) => - this._hoveredPolylinePointIndex === i.index ? [0, 0, 255, 255] : polyline.color, + this.state.hoveredPolylinePoint?.polylineId === polyline.id && + this.state.hoveredPolylinePoint.pointIndex === i.index + ? [230, 136, 21, 255] + : polyline.color, extruded: false, radius: 20, radiusUnits: "pixels", @@ -171,6 +217,9 @@ export class EditablePolylineLayer extends CompositeLayer { @@ -93,7 +93,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita new HoverPointLayer({ id: "hover-point-layer", point: hoverPoint, - color: [255, 255, 255, 100], + color: [230, 136, 21, 100], }), new EditablePolylineLayer({ id: "editable-polylines-layer", From bace0d3e892d924bf9012b4940db57ab5318b290 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 20 Jan 2025 09:05:06 +0100 Subject: [PATCH 07/97] wip --- frontend/package-lock.json | 76 ++-- frontend/package.json | 2 +- .../view/components/ReadoutWrapper.tsx | 10 +- .../deckGlLayers/EditablePolylineLayer.ts | 213 +++++++---- .../editablePolylinesHook.ts | 109 ------ .../editablePolylinesHook.tsx | 336 ++++++++++++++++++ .../subsurfaceViewerWithCameraState.tsx | 1 + 7 files changed, 526 insertions(+), 221 deletions(-) delete mode 100644 frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts create mode 100644 frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6dafa8805..bcef10758 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,7 +19,7 @@ "@tanstack/react-query-devtools": "^5.63", "@types/geojson": "^7946.0.14", "@webviz/group-tree-plot": "^1.1.14", - "@webviz/subsurface-viewer": "^1.1.1", + "@webviz/subsurface-viewer": "^1.5.1", "@webviz/well-completions-plot": "^1.5.11", "@webviz/well-log-viewer": "^1.12.7", "animate.css": "^4.1.1", @@ -543,9 +543,9 @@ } }, "node_modules/@deck.gl/aggregation-layers": { - "version": "9.0.33", - "resolved": "https://registry.npmjs.org/@deck.gl/aggregation-layers/-/aggregation-layers-9.0.33.tgz", - "integrity": "sha512-h0YCvdY4nZctkW1/1zFuwyL3o3o1HwZamRRdImpi74ug0GiLayt0kc4jLBbId4Tlxm5q/N2lggrZkFa9TPytWA==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/aggregation-layers/-/aggregation-layers-9.0.40.tgz", + "integrity": "sha512-tZ3NEDVlZnCnwbxdoB+qB184gSricnbcOZwkPHqNWk+2wadyd6Q0j9V9aZ9V6M5BGDn86DrOKPFygwmUl/jkwA==", "dependencies": { "@luma.gl/constants": "~9.0.27", "@luma.gl/shadertools": "~9.0.27", @@ -560,9 +560,9 @@ } }, "node_modules/@deck.gl/core": { - "version": "9.0.33", - "resolved": "https://registry.npmjs.org/@deck.gl/core/-/core-9.0.33.tgz", - "integrity": "sha512-KTfanNfb0b/JKV6BFSzQ8uMC07Yy5zvzLnnfHLV4l4ostHdEaPHdBEr5IDhsSRMBgcYSfsML3NpTFncVom+IyA==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/core/-/core-9.0.40.tgz", + "integrity": "sha512-NfBXRTuiqhYE42dOz73XnKaO7YZH8qsR1/9U/6lSV/XKnfrFsbhhRUePz3Ik6S3GbVpOoqCDuTFf0i3Tj+pxyw==", "dependencies": { "@loaders.gl/core": "^4.2.0", "@loaders.gl/images": "^4.2.0", @@ -583,9 +583,9 @@ } }, "node_modules/@deck.gl/extensions": { - "version": "9.0.33", - "resolved": "https://registry.npmjs.org/@deck.gl/extensions/-/extensions-9.0.33.tgz", - "integrity": "sha512-rxfz3/I4mf0WKW6EvntMmsn9F7rkkWYUweadUZSw12FQ50fm8GVDJ7k/2Mkzn1WJEocJW2Pv5mBRqsSWtUdyQQ==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/extensions/-/extensions-9.0.40.tgz", + "integrity": "sha512-1lESbg4NLkXxonO5f6aEX9a1DP4d8Vd+YLz9O4SwBJeZQZSeo4d3iJuJPL3MyA4thqdaA1Q9Vi8gtD9Mru6asg==", "dependencies": { "@luma.gl/constants": "~9.0.27", "@luma.gl/shadertools": "~9.0.27", @@ -598,9 +598,9 @@ } }, "node_modules/@deck.gl/geo-layers": { - "version": "9.0.33", - "resolved": "https://registry.npmjs.org/@deck.gl/geo-layers/-/geo-layers-9.0.33.tgz", - "integrity": "sha512-OmKfeliZclDIi24EfX/1geNR4gADKzRrF7M10oesREvTygkHdUxzeyEPpWqp0XS38qn80qfd3zttUN63+Vc0Bg==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/geo-layers/-/geo-layers-9.0.40.tgz", + "integrity": "sha512-kIxryoyWzicqLvAImtuUSX7NEntSdMIjCq0DoDdvSfhE37R0FxDRc5Vse5fjg9/1nNshw7mEcD2KxUS8MI8ypA==", "dependencies": { "@loaders.gl/3d-tiles": "^4.2.0", "@loaders.gl/gis": "^4.2.0", @@ -630,9 +630,9 @@ } }, "node_modules/@deck.gl/json": { - "version": "9.0.33", - "resolved": "https://registry.npmjs.org/@deck.gl/json/-/json-9.0.33.tgz", - "integrity": "sha512-ZxIxWyVYyh1RJcZ1UwA1jH/cqv+KcSL39nJ6r24z9twuIgtWO3ezeEtHTwT/mnQGMfdHWcFwMTaDXUG8pOO2Iw==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/json/-/json-9.0.40.tgz", + "integrity": "sha512-GAkENkzNPjEcHOR2xZJkgvzDWlqRhaU2Um5rmSdvm2FVYlfEpIWkfIWzroT5YrVOJ8nGC4pOYBXo0SLyNOwM9Q==", "dependencies": { "jsep": "^0.3.0" }, @@ -641,9 +641,9 @@ } }, "node_modules/@deck.gl/layers": { - "version": "9.0.33", - "resolved": "https://registry.npmjs.org/@deck.gl/layers/-/layers-9.0.33.tgz", - "integrity": "sha512-PBtxOuSq5QmlQ1N27FUIC3sC5iLFqAsdyEXUkjzWMPdLGjeow5xx32j3E0iag//ShgW4jiffS1dqgYywbjwYhg==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/layers/-/layers-9.0.40.tgz", + "integrity": "sha512-7emTkPLVWeVuV5VPBTmHDJegkGH1i76GuvhHNXikper/Zwljf9QVUacPqk3or4lHBCzzlkeccCsRmTd3l+MBYQ==", "dependencies": { "@loaders.gl/images": "^4.2.0", "@loaders.gl/schema": "^4.2.0", @@ -661,9 +661,9 @@ } }, "node_modules/@deck.gl/mesh-layers": { - "version": "9.0.33", - "resolved": "https://registry.npmjs.org/@deck.gl/mesh-layers/-/mesh-layers-9.0.33.tgz", - "integrity": "sha512-iN57iZ4Vq+/8polkVuJ7PsIOUpJbUVrRYywTGQgm8PaJ4WrN3wigo0fcrKfDzts7O2fluh37qg8aGNRBLuqMFQ==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/mesh-layers/-/mesh-layers-9.0.40.tgz", + "integrity": "sha512-aCYnxfXmTrod+YMC6cHN4fP/OBihkZi+hgllJkBDiKkFr83cktJJGfjruHYYyQQuBKUPI1J2LP6aUD5/vXO2MA==", "dependencies": { "@loaders.gl/gltf": "^4.2.0", "@luma.gl/gltf": "~9.0.27", @@ -676,9 +676,9 @@ } }, "node_modules/@deck.gl/react": { - "version": "9.0.33", - "resolved": "https://registry.npmjs.org/@deck.gl/react/-/react-9.0.33.tgz", - "integrity": "sha512-ka9QAfv3GxqQJtXjyGcIjMFR8d24dEaaQ53qSjjWgb8gLjb+RQSKZ7YB18VMX1g3iq7xyDRJ3LobcRA14m0SEA==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/react/-/react-9.0.40.tgz", + "integrity": "sha512-NiLgIMbgcsukDVKBNwyoE4e6Xtb43uR1WFSpfI2ZcL85BJMfzy7YWsSjJCN5jTdovMv7b9jkNxWWrKY702x3Tg==", "peerDependencies": { "@deck.gl/core": "^9.0.0", "react": ">=16.3.0", @@ -6753,20 +6753,20 @@ } }, "node_modules/@webviz/subsurface-viewer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@webviz/subsurface-viewer/-/subsurface-viewer-1.1.1.tgz", - "integrity": "sha512-+a9JFyCX3bUJpGceDgSFPNIMKyvTNhZ4OZi17gi0zAPZMacY5/0ZDnJNWpW9/DbBM26F5aKEg5XU7ALK/MbCrQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@webviz/subsurface-viewer/-/subsurface-viewer-1.5.1.tgz", + "integrity": "sha512-dq7Oq50jt38e9NkHqZ8TjFcEa8BzJ7P4mVzBW6Truz2MzN96vALJwpsXj5ivnsE5yFsalSTqou7qAla3mY7fpQ==", "dependencies": { "@deck.gl-community/editable-layers": "^9.0.3", - "@deck.gl/aggregation-layers": "^9.0.33", - "@deck.gl/core": "^9.0.33", - "@deck.gl/extensions": "^9.0.33", - "@deck.gl/geo-layers": "^9.0.33", - "@deck.gl/json": "^9.0.33", - "@deck.gl/layers": "^9.0.33", - "@deck.gl/mesh-layers": "^9.0.33", - "@deck.gl/react": "^9.0.33", - "@emerson-eps/color-tables": "^0.4.71", + "@deck.gl/aggregation-layers": "^9.0.40", + "@deck.gl/core": "^9.0.40", + "@deck.gl/extensions": "^9.0.40", + "@deck.gl/geo-layers": "^9.0.40", + "@deck.gl/json": "^9.0.40", + "@deck.gl/layers": "^9.0.40", + "@deck.gl/mesh-layers": "^9.0.40", + "@deck.gl/react": "^9.0.40", + "@emerson-eps/color-tables": "^0.4.85", "@equinor/eds-core-react": "^0.36.0", "@equinor/eds-icons": "^0.21.0", "@turf/simplify": "^7.1.0", @@ -6782,7 +6782,7 @@ "math.gl": "^4.0.1", "mathjs": "^13.2.0", "merge-refs": "^1.2.2", - "workerpool": "^9.1.3" + "workerpool": "^9.2.0" }, "peerDependencies": { "@mui/material": "^5.11", diff --git a/frontend/package.json b/frontend/package.json index b9b76e889..f184be76a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -30,7 +30,7 @@ "@tanstack/react-query-devtools": "^5.63", "@types/geojson": "^7946.0.14", "@webviz/group-tree-plot": "^1.1.14", - "@webviz/subsurface-viewer": "^1.1.1", + "@webviz/subsurface-viewer": "^1.5.1", "@webviz/well-completions-plot": "^1.5.11", "@webviz/well-log-viewer": "^1.12.7", "animate.css": "^4.1.1", diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index aa9ef5ce6..cb1fb26e1 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -19,6 +19,7 @@ export type ReadooutWrapperProps = { export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const id = React.useId(); + // const deckGlRef = React.useRef(null); const [cameraPositionSetByAction, setCameraPositionSetByAction] = React.useState(null); const [triggerHomeCounter, setTriggerHomeCounter] = React.useState(0); @@ -27,7 +28,12 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const [verticalScale, setVerticalScale] = React.useState(1); const [polylineEditingActive, setPolylineEditingActive] = React.useState(false); - const { onMouseEvent, layers } = useEditablePolylines({ polylines: [], editingActive: polylineEditingActive }); + const { onMouseEvent, layers, cursorIcon, disableCameraInteraction } = useEditablePolylines({ + polylines: [], + editingActive: polylineEditingActive, + }); + + // deckGlRef.current?.deck.setProps({...deckGlRef.props}) function handleFitInViewClick() { setTriggerHomeCounter((prev) => prev + 1); @@ -73,6 +79,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { onVerticalScaleChange={handleVerticalScaleChange} verticalScale={verticalScale} /> + {cursorIcon} setCameraPositionSetByAction(null)} onMouseEvent={handleMouseEvent} + userCameraInteractionActive={!disableCameraInteraction} layers={adjustedLayers} verticalScale={verticalScale} scale={{ diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts index c7b39c9ff..13170081c 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts @@ -1,36 +1,51 @@ -import { CompositeLayer, Layer, LayerContext, PickingInfo } from "@deck.gl/core"; +import { CompositeLayer, GetPickingInfoParams, Layer, LayerContext, PickingInfo } from "@deck.gl/core"; import { PathStyleExtension } from "@deck.gl/extensions"; -import { ColumnLayer } from "@deck.gl/layers"; +import { ColumnLayer, LineLayer, PathLayer } from "@deck.gl/layers"; import { AnimatedPathLayer } from "./AnimatedPathLayer"; export type EditablePolylineLayerProps = { id: string; - editablePolylineId: string | null; - polylines: Polyline[]; + polyline: EditablePolyline; + mouseHoverPoint?: number[]; }; -export type Polyline = { - id: string; +export type EditablePolyline = { color: [number, number, number, number]; - polyline: number[][]; + path: number[][]; + referencePathPointIndex?: number; }; +export type EditablePolylineLayerPickingInfo = PickingInfo & { + editableEntity?: { + type: "line" | "point"; + index: number; + }; +}; + +export function isEditablePolylineLayerPickingInfo(info: PickingInfo): info is EditablePolylineLayerPickingInfo { + return ( + Object.keys(info).includes("editableEntity") && + ((info as EditablePolylineLayerPickingInfo).editableEntity?.type === "line" || + (info as EditablePolylineLayerPickingInfo).editableEntity?.type === "point") + ); +} + export class EditablePolylineLayer extends CompositeLayer { static layerName: string = "EditablePolylineLayer"; // @ts-expect-error state!: { - hoveredPolylinePoint: { - polylineId: string; - pointIndex: number; + hoveredEntity: { + layer: "line" | "point"; + index: number; } | null; dashStart: number; }; initializeState(context: LayerContext): void { this.state = { - hoveredPolylinePoint: null, + hoveredEntity: null, dashStart: 0, }; } @@ -129,97 +144,151 @@ export class EditablePolylineLayer extends CompositeLayer polyline.id === id); - } - - private extractPolylineIdFromSourceLayerId(sourceLayerId: string): string | null { - const match = sourceLayerId.match(/points-(.*)/); - if (!match) { - return null; + getPickingInfo({ info }: GetPickingInfoParams): EditablePolylineLayerPickingInfo { + if (info && info.sourceLayer && info.index !== undefined && info.index !== -1) { + let layer: "line" | "point" | null = null; + if (info.sourceLayer.id.includes("lines-selection")) { + layer = "line"; + } else if (info.sourceLayer.id.includes("points")) { + layer = "point"; + } + return { + ...info, + editableEntity: layer + ? { + type: layer, + index: info.index, + } + : undefined, + }; } - return match[1]; + return info; } - onHover(info: PickingInfo): boolean { - const sourceLayerId = info.sourceLayer?.id; - if (info.index !== undefined && sourceLayerId) { - const polylineId = this.extractPolylineIdFromSourceLayerId(sourceLayerId); - if (!polylineId) { - return false; - } - - const polyline = this.getPolylineById(polylineId); - if (!polyline) { - return false; - } - + onHover(info: EditablePolylineLayerPickingInfo): boolean { + if (!info.editableEntity) { this.setState({ - hoveredPolylinePoint: { - polylineId, - pointIndex: info.index, - }, + hoveredEntity: null, }); - - return true; + return false; } this.setState({ - hoveredPolylinePoint: null, + hoveredEntity: { + layer: info.editableEntity.type, + index: info.index, + }, }); return false; } renderLayers() { + const { polyline, mouseHoverPoint } = this.props; + const layers: Layer[] = []; - for (const polyline of this.props.polylines) { - const polylineData: { from: number[]; to: number[] }[] = []; - for (let i = 0; i < polyline.polyline.length - 1; i++) { - polylineData.push({ from: polyline.polyline[i], to: polyline.polyline[i + 1] }); - } + const polylinePathLayerData: number[][][] = []; + for (let i = 0; i < polyline.path.length - 1; i++) { + polylinePathLayerData.push([polyline.path[i], polyline.path[i + 1]]); + } + + layers.push( + new AnimatedPathLayer({ + id: "lines", + data: polylinePathLayerData, + dashStart: 0, + getColor: polyline.color, + getPath: (d) => d, + getDashArray: [10, 10], + getWidth: 10, + billboard: true, + widthUnits: "meters", + extensions: [new PathStyleExtension({ highPrecisionDash: true })], + parameters: { + // @ts-expect-error - deck.gl types are wrong + depthTest: false, + }, + pickable: false, + depthTest: false, + }), + new PathLayer({ + id: "lines-selection", + data: polylinePathLayerData, + getColor: [0, 0, 0, 0], + getPath: (d) => d, + getWidth: 50, + billboard: true, + widthUnits: "meters", + parameters: { + depthTest: false, + }, + pickable: true, + }), + new ColumnLayer({ + id: "points", + data: polyline.path, + getElevation: 1, + getPosition: (d) => d, + getFillColor: (d, context) => { + if (context.index === polyline.referencePathPointIndex) { + return [230, 136, 21, 255]; + } + return [255, 255, 255, 255]; + }, + getLineColor: [230, 136, 21, 255], + getLineWidth: (d, context) => { + if ( + this.state.hoveredEntity && + this.state.hoveredEntity.layer === "point" && + context.index === this.state.hoveredEntity.index + ) { + return 20; + } + return 10; + }, + stroked: true, + extruded: false, + radius: 20, + radiusUnits: "pixels", + pickable: true, + parameters: { + depthTest: false, + }, + updateTriggers: { + getFillColor: [this.state.hoveredEntity, polyline.referencePathPointIndex], + getLineWidth: [this.state.hoveredEntity, polyline.referencePathPointIndex], + }, + }) + ); + + if (polyline.referencePathPointIndex !== undefined && mouseHoverPoint && this.state.hoveredEntity === null) { layers.push( - new AnimatedPathLayer({ - id: `lines-${polyline.id}`, - data: [polyline.polyline], - dashStart: 0, - getColor: polyline.color, - getPath: (d) => d, - getDashArray: [10, 10], - getWidth: 10, - billboard: true, - widthUnits: "meters", - extensions: [new PathStyleExtension({ highPrecisionDash: true })], + new LineLayer({ + id: "line", + data: [{ from: polyline.path[polyline.referencePathPointIndex], to: mouseHoverPoint }], + getSourcePosition: (d) => d.from, + getTargetPosition: (d) => d.to, + getColor: [230, 136, 21, 100], + getWidth: 2, parameters: { - // @ts-expect-error - deck.gl types are wrong depthTest: false, }, - depthTest: false, - }) - ); - layers.push( + }), new ColumnLayer({ - id: `points-${polyline.id}`, - data: polyline.polyline, + id: "hover-point", + data: [mouseHoverPoint], getElevation: 1, getPosition: (d) => d, - getFillColor: (d, i) => - this.state.hoveredPolylinePoint?.polylineId === polyline.id && - this.state.hoveredPolylinePoint.pointIndex === i.index - ? [230, 136, 21, 255] - : polyline.color, + getFillColor: [230, 136, 21, 255], extruded: false, radius: 20, radiusUnits: "pixels", - pickable: true, + pickable: false, parameters: { depthTest: false, }, - updateTriggers: { - getFillColor: [this.state.hoveredPolylinePoint], - }, }) ); } diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts deleted file mode 100644 index df356d020..000000000 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.ts +++ /dev/null @@ -1,109 +0,0 @@ -import React from "react"; - -import { Layer } from "@deck.gl/core"; -import { MapMouseEvent } from "@webviz/subsurface-viewer"; - -import { isEqual } from "lodash"; -import { v4 } from "uuid"; - -import { EditablePolylineLayer } from "./deckGlLayers/EditablePolylineLayer"; -import { HoverPointLayer } from "./deckGlLayers/HoverPointLayer"; -import { Polyline } from "./types"; - -export type UseEditablePolylinesProps = { - editingActive: boolean; - polylines: Polyline[]; -}; - -export type UseEditablePolylinesReturnType = { - layers: Layer[]; - onMouseEvent: (event: MapMouseEvent) => void; -}; - -export function useEditablePolylines(props: UseEditablePolylinesProps): UseEditablePolylinesReturnType { - const [hoverPoint, setHoverPoint] = React.useState(null); - const [activePolylineId, setActivePolylineId] = React.useState(null); - const [polylines, setPolylines] = React.useState(props.polylines); - const [prevPolylines, setPrevPolylines] = React.useState(props.polylines); - - if (!isEqual(props.polylines, prevPolylines)) { - setPolylines(props.polylines); - setPrevPolylines(props.polylines); - } - - const onMouseEvent = React.useCallback( - (event: MapMouseEvent) => { - if (event.type === "hover") { - if (!props.editingActive) { - setHoverPoint(null); - return; - } - if (!event.x || !event.y) { - setHoverPoint(null); - return; - } - const firstLayerInfos = event.infos[0]; - if (firstLayerInfos && firstLayerInfos.coordinate) { - setHoverPoint([...firstLayerInfos.coordinate]); - } - } - - if (event.type === "click") { - if (!props.editingActive) { - return; - } - - const firstLayerInfos = event.infos[0]; - if (!firstLayerInfos || !firstLayerInfos.coordinate) { - return; - } - - if (!activePolylineId) { - const uuid = v4(); - setActivePolylineId(uuid); - setPolylines([ - ...polylines, - { id: uuid, color: [230, 136, 21, 255], polyline: [[...firstLayerInfos.coordinate]] }, - ]); - } else { - const updatedPolylines = polylines.map((polyline) => { - if (polyline.id === activePolylineId) { - if (!event.x || !event.y) { - return polyline; - } - const firstLayerInfos = event.infos[0]; - if (!firstLayerInfos || !firstLayerInfos.coordinate) { - return polyline; - } - return { - ...polyline, - polyline: [...polyline.polyline, [...firstLayerInfos.coordinate]], - }; - } - return polyline; - }); - setPolylines(updatedPolylines); - } - } - }, - [props.editingActive, activePolylineId, polylines] - ); - - const layers = [ - new HoverPointLayer({ - id: "hover-point-layer", - point: hoverPoint, - color: [230, 136, 21, 100], - }), - new EditablePolylineLayer({ - id: "editable-polylines-layer", - polylines, - editablePolylineId: activePolylineId, - }), - ]; - - return { - layers, - onMouseEvent, - }; -} diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx new file mode 100644 index 000000000..a0bcbc6ab --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx @@ -0,0 +1,336 @@ +import React from "react"; + +import { Layer, PickingInfo } from "@deck.gl/core"; +import { Add, Polyline as PolylineIcon, Remove } from "@mui/icons-material"; +import { MapMouseEvent } from "@webviz/subsurface-viewer"; + +import { isEqual } from "lodash"; + +import { + EditablePolyline, + EditablePolylineLayer, + isEditablePolylineLayerPickingInfo, +} from "./deckGlLayers/EditablePolylineLayer"; +import { Polyline } from "./types"; + +export type UseEditablePolylinesProps = { + editingActive: boolean; + polylines: Polyline[]; +}; + +export type UseEditablePolylinesReturnType = { + layers: Layer[]; + onMouseEvent: (event: MapMouseEvent) => void; + cursorIcon: React.ReactNode | null; + disableCameraInteraction: boolean; +}; + +enum CursorIcon { + ADD_POINT = "add-point", + REMOVE_POINT = "remove-point", + CONTINUE_FROM_POINT = "continue-from-point", +} + +enum PathAppendLocation { + START = "start", + END = "end", +} + +export function useEditablePolylines(props: UseEditablePolylinesProps): UseEditablePolylinesReturnType { + const [hoverPoint, setHoverPoint] = React.useState(null); + const [activePolyline, setActivePolyline] = React.useState(null); + const [polylines, setPolylines] = React.useState(props.polylines); + const [prevPolylines, setPrevPolylines] = React.useState(props.polylines); + const [cursorIcon, setCursorIcon] = React.useState(null); + const [cursorPosition, setCursorPosition] = React.useState(null); + const [appendLocation, setAppendLocation] = React.useState(PathAppendLocation.END); + const [draggedPointIndex, setDraggedPointIndex] = React.useState(null); + + if (!isEqual(props.polylines, prevPolylines)) { + setPolylines(props.polylines); + setPrevPolylines(props.polylines); + } + + React.useEffect(function onMount() { + function onKeyUp(event: KeyboardEvent) { + if (event.key === "Escape") { + setActivePolyline((prev) => (!prev ? prev : { ...prev, referencePathPointIndex: undefined })); + } + } + + window.addEventListener("keyup", onKeyUp); + + return () => { + window.removeEventListener("keyup", onKeyUp); + }; + }, []); + + const onMouseEvent = React.useCallback( + (event: MapMouseEvent) => { + if (event.type === "hover") { + if (!event.x || !event.y) { + setHoverPoint(null); + return; + } + + const firstLayerInfos = event.infos[0]; + + if (!firstLayerInfos) { + setHoverPoint(null); + setCursorPosition(null); + return; + } + + const editablePolylineLayerPickingInfo = event.infos.find(isEditablePolylineLayerPickingInfo); + + if ( + editablePolylineLayerPickingInfo && + editablePolylineLayerPickingInfo.viewport && + firstLayerInfos.coordinate + ) { + const position = editablePolylineLayerPickingInfo.viewport.project([...firstLayerInfos.coordinate]); + setCursorPosition(position); + if ( + editablePolylineLayerPickingInfo.editableEntity && + editablePolylineLayerPickingInfo.editableEntity.type === "line" + ) { + setCursorIcon(CursorIcon.ADD_POINT); + return; + } + if ( + activePolyline && + editablePolylineLayerPickingInfo.editableEntity && + editablePolylineLayerPickingInfo.editableEntity.type === "point" + ) { + const index = editablePolylineLayerPickingInfo.editableEntity.index; + if ( + activePolyline?.referencePathPointIndex === undefined && + (index === 0 || index === activePolyline.path.length - 1) + ) { + setCursorIcon(CursorIcon.CONTINUE_FROM_POINT); + return; + } + + setCursorIcon(CursorIcon.REMOVE_POINT); + return; + } + } else { + setCursorPosition(null); + } + + if (firstLayerInfos && firstLayerInfos.coordinate) { + setHoverPoint([...firstLayerInfos.coordinate]); + } + } + + if (event.type === "click") { + if (!props.editingActive) { + return; + } + + const firstLayerInfos = event.infos[0]; + if (!firstLayerInfos || !firstLayerInfos.coordinate) { + return; + } + + const editablePolylineLayerPickingInfo = event.infos.find(isEditablePolylineLayerPickingInfo); + if ( + activePolyline && + editablePolylineLayerPickingInfo && + editablePolylineLayerPickingInfo.editableEntity + ) { + // Remove point + if (editablePolylineLayerPickingInfo.editableEntity.type === "point") { + if (activePolyline.referencePathPointIndex === undefined) { + let index = editablePolylineLayerPickingInfo.editableEntity.index; + if (index === 0 || index === activePolyline.path.length - 1) { + const newPolyline: EditablePolyline = { + ...activePolyline, + referencePathPointIndex: index, + }; + + setActivePolyline(newPolyline); + if (index === 0) { + setAppendLocation(PathAppendLocation.START); + } else { + setAppendLocation(PathAppendLocation.END); + } + return; + } + } + + let newReferencePathPointIndex: number | undefined = undefined; + if (activePolyline.referencePathPointIndex !== undefined) { + newReferencePathPointIndex = Math.max(0, activePolyline.referencePathPointIndex - 1); + if ( + editablePolylineLayerPickingInfo.editableEntity.index > + activePolyline.referencePathPointIndex + ) { + newReferencePathPointIndex = activePolyline.referencePathPointIndex; + } + if (activePolyline.path.length - 1 < 1) { + newReferencePathPointIndex = undefined; + } + } + const newPolyline: EditablePolyline = { + ...activePolyline, + path: activePolyline.path.filter( + (_, index) => index !== editablePolylineLayerPickingInfo.editableEntity?.index + ), + referencePathPointIndex: newReferencePathPointIndex, + }; + + setActivePolyline(newPolyline); + return; + } + if (editablePolylineLayerPickingInfo.editableEntity.type === "line") { + const newPath = [...activePolyline.path]; + newPath.splice(editablePolylineLayerPickingInfo.editableEntity.index + 1, 0, [ + ...firstLayerInfos.coordinate, + ]); + + let newReferencePathPointIndex: number | undefined = undefined; + if (activePolyline.referencePathPointIndex !== undefined) { + newReferencePathPointIndex = activePolyline.referencePathPointIndex + 1; + if ( + editablePolylineLayerPickingInfo.editableEntity.index > + activePolyline.referencePathPointIndex + ) { + newReferencePathPointIndex = activePolyline.referencePathPointIndex; + } + } + + const newPolyline: EditablePolyline = { + ...activePolyline, + path: newPath, + referencePathPointIndex: newReferencePathPointIndex, + }; + + setActivePolyline(newPolyline); + return; + } + } + + if (!activePolyline) { + const newPolyline: EditablePolyline = { + color: [230, 136, 21, 255], + path: [[...firstLayerInfos.coordinate]], + referencePathPointIndex: 0, + }; + setActivePolyline(newPolyline); + } else { + if (activePolyline.referencePathPointIndex === undefined) { + return; + } + setActivePolyline({ + ...activePolyline, + path: appendCoordinateToPath(activePolyline.path, firstLayerInfos.coordinate, appendLocation), + referencePathPointIndex: + appendLocation === PathAppendLocation.END ? activePolyline.path.length : 0, + }); + } + } + }, + [props.editingActive, activePolyline, appendLocation] + ); + + const onDragStart = React.useCallback(function onDragStart(info: PickingInfo) { + if (!isEditablePolylineLayerPickingInfo(info)) { + return true; + } + + if (info.editableEntity?.type === "point") { + setDraggedPointIndex(info.editableEntity.index); + } + + return false; + }, []); + + const onDrag = React.useCallback( + function onDrag(info: PickingInfo) { + if (draggedPointIndex === null || !activePolyline || !info.coordinate) { + return true; + } + + const newPath = [...activePolyline.path]; + newPath[draggedPointIndex] = [ + info.coordinate[0], + info.coordinate[1], + activePolyline.path[draggedPointIndex][2], + ]; + + setActivePolyline({ + ...activePolyline, + path: newPath, + }); + + return false; + }, + [draggedPointIndex, activePolyline] + ); + + const onDragEnd = React.useCallback(function onDragEnd(info: PickingInfo) { + setDraggedPointIndex(null); + }, []); + + const layers: Layer[] = []; + + if (activePolyline) { + layers.push( + new EditablePolylineLayer({ + id: "editable-polylines-layer", + polyline: activePolyline, + mouseHoverPoint: hoverPoint ?? undefined, + onDragStart, + onDragEnd, + onDrag, + }) + ); + } + + const cursorIconElement = + cursorIcon && cursorPosition ? ( +
+ {cursorIcon === CursorIcon.CONTINUE_FROM_POINT ? ( + + ) : cursorIcon === CursorIcon.ADD_POINT ? ( + + ) : cursorIcon === CursorIcon.REMOVE_POINT ? ( + + ) : null} +
+ ) : null; + + return { + layers, + onMouseEvent, + cursorIcon: cursorIconElement, + disableCameraInteraction: draggedPointIndex !== null, + }; +} + +function appendCoordinateToPath(path: number[][], coordinate: number[], index: number): number[][]; +function appendCoordinateToPath(path: number[][], coordinate: number[], appendLocation: PathAppendLocation): number[][]; +function appendCoordinateToPath( + path: number[][], + coordinate: number[], + indexOrAppendLocation: number | PathAppendLocation +): number[][] { + if (typeof indexOrAppendLocation === "number") { + return [...path.slice(0, indexOrAppendLocation), coordinate, ...path.slice(indexOrAppendLocation)]; + } + + if (indexOrAppendLocation === PathAppendLocation.START) { + return [coordinate, ...path]; + } + + return [...path, coordinate]; +} diff --git a/frontend/src/modules/_shared/components/SubsurfaceViewerWithCameraState/subsurfaceViewerWithCameraState.tsx b/frontend/src/modules/_shared/components/SubsurfaceViewerWithCameraState/subsurfaceViewerWithCameraState.tsx index 03af3816f..fe6a1b522 100644 --- a/frontend/src/modules/_shared/components/SubsurfaceViewerWithCameraState/subsurfaceViewerWithCameraState.tsx +++ b/frontend/src/modules/_shared/components/SubsurfaceViewerWithCameraState/subsurfaceViewerWithCameraState.tsx @@ -39,6 +39,7 @@ export function SubsurfaceViewerWithCameraState(props: SubsurfaceViewerWithCamer const handleCameraChange = React.useCallback( function handleCameraChange(viewport: ViewStateType): void { if (props.userCameraInteractionActive || props.userCameraInteractionActive === undefined) { + console.debug("Camera position changed", viewport); setCameraPosition(viewport); } }, From 78f7a5e193a5b2cd1c762b2d60b8be7ffa9cbc07 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 20 Jan 2025 17:29:31 +0100 Subject: [PATCH 08/97] wip --- frontend/package-lock.json | 8 +- frontend/package.json | 2 +- frontend/src/assets/add_path.ico | Bin 0 -> 4286 bytes .../src/assets/add_path.ico:Zone.Identifier | 4 + frontend/src/assets/add_path.svg | 155 +++++++++++++++ frontend/src/assets/continue_path.ico | Bin 0 -> 4286 bytes .../assets/continue_path.ico:Zone.Identifier | 4 + frontend/src/assets/continue_path.svg | 157 +++++++++++++++ frontend/src/assets/remove_path.ico | Bin 0 -> 4286 bytes .../assets/remove_path.ico:Zone.Identifier | 4 + frontend/src/assets/remove_path.svg | 156 +++++++++++++++ frontend/src/assets/set_path_point.svg | 154 +++++++++++++++ .../view/components/ReadoutWrapper.tsx | 19 +- .../3DViewerNew/view/components/Toolbar.tsx | 7 +- .../deckGlLayers/EditablePolylineLayer.ts | 114 ++--------- .../deckGlLayers/PolylinesLayer.ts | 57 ++++++ .../editablePolylinesHook.tsx | 185 ++++++++++++++---- 17 files changed, 873 insertions(+), 153 deletions(-) create mode 100644 frontend/src/assets/add_path.ico create mode 100644 frontend/src/assets/add_path.ico:Zone.Identifier create mode 100644 frontend/src/assets/add_path.svg create mode 100644 frontend/src/assets/continue_path.ico create mode 100644 frontend/src/assets/continue_path.ico:Zone.Identifier create mode 100644 frontend/src/assets/continue_path.svg create mode 100644 frontend/src/assets/remove_path.ico create mode 100644 frontend/src/assets/remove_path.ico:Zone.Identifier create mode 100644 frontend/src/assets/remove_path.svg create mode 100644 frontend/src/assets/set_path_point.svg create mode 100644 frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bcef10758..4a4ada631 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,7 +19,7 @@ "@tanstack/react-query-devtools": "^5.63", "@types/geojson": "^7946.0.14", "@webviz/group-tree-plot": "^1.1.14", - "@webviz/subsurface-viewer": "^1.5.1", + "@webviz/subsurface-viewer": "^1.6.0", "@webviz/well-completions-plot": "^1.5.11", "@webviz/well-log-viewer": "^1.12.7", "animate.css": "^4.1.1", @@ -6753,9 +6753,9 @@ } }, "node_modules/@webviz/subsurface-viewer": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@webviz/subsurface-viewer/-/subsurface-viewer-1.5.1.tgz", - "integrity": "sha512-dq7Oq50jt38e9NkHqZ8TjFcEa8BzJ7P4mVzBW6Truz2MzN96vALJwpsXj5ivnsE5yFsalSTqou7qAla3mY7fpQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@webviz/subsurface-viewer/-/subsurface-viewer-1.6.0.tgz", + "integrity": "sha512-zSbjmN3+ommP9K6O/QIZjtvQm+6aWboFfsT1kx4S/viSohN4piUefJrRh4Eu2oxpEaZ6s5z+qY2kQkC4EDsMqw==", "dependencies": { "@deck.gl-community/editable-layers": "^9.0.3", "@deck.gl/aggregation-layers": "^9.0.40", diff --git a/frontend/package.json b/frontend/package.json index f184be76a..79736ee08 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -30,7 +30,7 @@ "@tanstack/react-query-devtools": "^5.63", "@types/geojson": "^7946.0.14", "@webviz/group-tree-plot": "^1.1.14", - "@webviz/subsurface-viewer": "^1.5.1", + "@webviz/subsurface-viewer": "^1.6.0", "@webviz/well-completions-plot": "^1.5.11", "@webviz/well-log-viewer": "^1.12.7", "animate.css": "^4.1.1", diff --git a/frontend/src/assets/add_path.ico b/frontend/src/assets/add_path.ico new file mode 100644 index 0000000000000000000000000000000000000000..85471e5b8d5f8bacab70a18fe14e6da9048d687f GIT binary patch literal 4286 zcmdT|+fSQi82|hayfSX%IF4g-nlM(FW=Eimxv5d7n{#%cX4cJkp&4kh3^%}zc*VTH z12A0{baY+}v*Anz2BP zM04}^|G5g&)6+1#T!3{ChoHK;HYoT7$6rCnhH%u@T=E-#e0&^JQ&abSer9F{g$1v! z3ZF5&V5-206GmLUYR1680Lb%2E~g9i>N_>6uW9dRpH?F$I}c%@kubh~5}#lCLipTY zX+8`L{El5a_h7^NFhobkVdswBC@Ojvjg8-8XlTeQxB2e2j4Z5Q7b-T>@8Mw^5fL6G zI`El``S6DEl-F5b_K;^bUqeiEyvWexgoZ>QE;a%C@}5BH`7*S%Sui;{35VlPyz};H z95)n#acAzOJ(?jpWk?a}O@4%!L}i3D}oy(=4byIo9R%z2J$ zY-~()pv{a0`+&ChZ!r`he}7OG<9X~Q1Gc25iw?x|)P>K5g|A^^V#4#Nd?%K_`A^;J z>b^ol!#C*fx8diXe!<1ckMQg>hw#YeWQixSH%7+b^5rW_u*o34!B8OnW6rIVTrL+( zrVEIT*@Rd%_Bq*ll$?DZw{JJWX0xNEr4{AnCg}C~*t;heHdQ}gME@}#n3JrXm5@`d zq4Vd;B|}iz)LzEgb~>Fn^xR?THyS?oUwHnA?0L%Z_j_k&*Fv8m zMx^Td+FJMAv{)=lVnh6+FCN3_=%~MtUgx^Hx>bK~75f?6OxBZJ` zoycbosIPbTtfr=B={>~d;yh$8rfz{G_OpzPOvy3MeBL!kB_-#)d|zDrp3JsDVpCRM zUq5u)w#yx%=yb8n%fZ1xweIp4dNAh+d22zOb2{C2b?zkK#*IdSxOuY?@o}5QC;Rgc zfH7JtvfJ!pzdm=L*vR=oU-RxIx!w}ET=!*z-Ts@3pC$3;{=IvDsCS#aGViYiIM;pQ zkBp4S?7eg6N7*JOXKrQX#enC!FMRTNFVp&YUEP-`EiDJ{)xjXv_tfP5|J@M`UH`9` K+W!H9_4_Xz;;keA literal 0 HcmV?d00001 diff --git a/frontend/src/assets/add_path.ico:Zone.Identifier b/frontend/src/assets/add_path.ico:Zone.Identifier new file mode 100644 index 000000000..4ea481bc6 --- /dev/null +++ b/frontend/src/assets/add_path.ico:Zone.Identifier @@ -0,0 +1,4 @@ +[ZoneTransfer] +ZoneId=3 +ReferrerUrl=https://cloudconvert.com/ +HostUrl=https://eu-central.storage.cloudconvert.com/tasks/59c77e3f-77e4-4f93-af94-9405785612a8/add_path.ico?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=cloudconvert-production%2F20250120%2Ffra%2Fs3%2Faws4_request&X-Amz-Date=20250120T144016Z&X-Amz-Expires=86400&X-Amz-Signature=68edd04f71aac995e34d7544ae364e4b35488bf55946b4437765960cb73a9901&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3D%22add_path.ico%22&response-content-type=image%2Fvnd.microsoft.icon&x-id=GetObject diff --git a/frontend/src/assets/add_path.svg b/frontend/src/assets/add_path.svg new file mode 100644 index 000000000..4d70f9fee --- /dev/null +++ b/frontend/src/assets/add_path.svg @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/continue_path.ico b/frontend/src/assets/continue_path.ico new file mode 100644 index 0000000000000000000000000000000000000000..939acffc98bf94551a5dd791910f6ce4d4941232 GIT binary patch literal 4286 zcmd^D=~L8Y9RKwn^d?j8V>!gQ|NqK0TjjXIDrNXqMqcU*_50Z=d7)`QG1$Rg|a1 zpOcfqPvzp%in2~o6i+e4l(J0>&Px(&u0Ez=notz1GL4UqV`O9mM&r~fBRyh2Gc$w9 z$w^F2O`)LRGsMRy;QI9{xq)-5O;uGtBQx_9?%wUe@#D$Zu-*=yLXm{q^70`zFHUz25@d6 zdN}wHUw!#Ca&q#pJ33b4Q3msHQ+Xogja2047vRSqDwbsfgW)fPZQlV~8waR_4%@ed zBO~J@(8sj(Tsu;>;9xb@KWhsIduI!-8J|!3W!*D2NIP+I-r0rp;ha`mE^Oy%;YGgm zp@+Md(D5bYoXbUBoemQd6Brm6l(x#u_!x0qa_dY=XgMQ22 zC%rIN%Y@I5y_Sg0{y|a(>drVa^W^vBKY@xiaQxXAXPeXT}}l zhqa}!@N?*dkK5ZjaOY0FsO`o@SrTt>aBx}mgL-FWorcQQR4dFrHa|_WI7wN&z5Q_b z&`~rrG%kw0(uKyxCiwbno)=%lo*Qp-WuZ+lBJ=&< zau7c^H-AxnD_x-ee!h?NCmykH-1r5IpXjJqX?wE`Sj*l{e;4ex)}W@QX88LBTJ&dr zB_<}z+#Vht#)0@lvi6zrxt?(FFt}z7YHn_^)EQzp3)`2Ln*6KN=^pb9@sA!kCUeai z`0R^;qHogf^lhm61(9RzlC?(kK+c(U=lhn>7g1YVXANk{qqVgSL83+ypSrR3vzGMt z_hav#eXhn)mug1@xrxhru#DQ&7K~cq)lRD_DCPC1~z%>yYy23sL0(?N8YVyYu>$Ntk+mC z))&%2ukRN4Tcp3ae(&Dj2oH+@`+d2UV|^j~fq? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/remove_path.ico b/frontend/src/assets/remove_path.ico new file mode 100644 index 0000000000000000000000000000000000000000..ffbee10c5bb5eb827359b6d99ba750d9a147f341 GIT binary patch literal 4286 zcmeHL$xl;Z5P$v$o{Y#=WEEsnuoloLh*HHp?t1}ahz8?9BWPko1uVo9dN3w}i7}{D z4`LKC5e!f@YOw6knp8aiLPE>F{QPE`)*>Ywe54nqnf7(Q_vSa-H}kcUv_QNR3W?sO zM++rskt9hWf`v$0Cz#|+V$DCo#x{W@VV>qAFiQjm2L~}SGBV3ubF(}?K8`|d5i&DR zm3&$aiO3=zB)S zDGUt_IY0W}*8>9sC@Xt{ii(%$>@?!b=dZY3dAKQ?30TuVh`Q8p3gN$ zaVGW_c_x2DmxW{Jj@>9Kx&^)d1KQg=(9rOWb1erx`F-WeRRs74Psy>p-fXrg*p~3{ zC>%a?6b3`19MG(zv9Sq}5i6!)JJ*}DEfkXy(P!i$gTa7^@aQS{R9`VGmAv2owS(~U z^KZ%p5Ar`M@~{7dN7z+WZ}H+q1vaal`K~CSV&p zFQdI}u~@Kw-y!aOc6?etc;GOv$zecqbBm+T5Qd+KeQBvJzk0oXT5bsc=#k^-@9&ob z9xW}cBHpXmf68r~YK_=q?W|iXW+2JzzSDJl+*;Jt)yslSN?Kdn5F4XpeDaNE6!qZV z-d^n3zKi<+;Zt2uo$7SYWy2;#ZEfvX6{}+X)C0=Pb*#U-x`ulX;S$q4q+U$6$w}1D zl*%}+F`D^w)*#Bwy*?%OIXRa>^FeOv=;(xMbpoFovM=WFeD3P%68kRI(StRbbdYQ= zh~}J1l_2I$C|Xa?)Bb}`%)^zsbTCbI_ z)?N8vGJO~L@tkj3|M~M5k`j}7-anJM)?MNE_4V=W{rK?{FB2xs+~VTfve&vRe3H?* l%-+w-${r&xuK;web_db^9vODe?+$nP>kdr&{{Zg#`~yBv+}HpB literal 0 HcmV?d00001 diff --git a/frontend/src/assets/remove_path.ico:Zone.Identifier b/frontend/src/assets/remove_path.ico:Zone.Identifier new file mode 100644 index 000000000..86089d6d7 --- /dev/null +++ b/frontend/src/assets/remove_path.ico:Zone.Identifier @@ -0,0 +1,4 @@ +[ZoneTransfer] +ZoneId=3 +ReferrerUrl=https://cloudconvert.com/ +HostUrl=https://eu-central.storage.cloudconvert.com/tasks/67f45301-df20-46b1-b659-ce98e2d6ecf9/remove_path.ico?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=cloudconvert-production%2F20250120%2Ffra%2Fs3%2Faws4_request&X-Amz-Date=20250120T144015Z&X-Amz-Expires=86400&X-Amz-Signature=3c1fd47ed0dc0f26b79d3d21d5543bb4efb6ce4045776fe9b0ab8b9f30f5e436&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3D%22remove_path.ico%22&response-content-type=image%2Fvnd.microsoft.icon&x-id=GetObject diff --git a/frontend/src/assets/remove_path.svg b/frontend/src/assets/remove_path.svg new file mode 100644 index 000000000..57334da7f --- /dev/null +++ b/frontend/src/assets/remove_path.svg @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/set_path_point.svg b/frontend/src/assets/set_path_point.svg new file mode 100644 index 000000000..b3ef1a84d --- /dev/null +++ b/frontend/src/assets/set_path_point.svg @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index cb1fb26e1..874bb7285 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -1,6 +1,7 @@ import React from "react"; -import { Layer as DeckGlLayer } from "@deck.gl/core"; +import { Layer as DeckGlLayer, PickingInfo } from "@deck.gl/core"; +import { DeckGLRef } from "@deck.gl/react"; import { SubsurfaceViewerWithCameraState } from "@modules/_shared/components/SubsurfaceViewerWithCameraState"; import { BoundingBox3D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; @@ -19,7 +20,7 @@ export type ReadooutWrapperProps = { export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const id = React.useId(); - // const deckGlRef = React.useRef(null); + const deckGlRef = React.useRef(null); const [cameraPositionSetByAction, setCameraPositionSetByAction] = React.useState(null); const [triggerHomeCounter, setTriggerHomeCounter] = React.useState(0); @@ -28,7 +29,8 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const [verticalScale, setVerticalScale] = React.useState(1); const [polylineEditingActive, setPolylineEditingActive] = React.useState(false); - const { onMouseEvent, layers, cursorIcon, disableCameraInteraction } = useEditablePolylines({ + const { onMouseEvent, layers, onDrag, getCursor } = useEditablePolylines({ + deckGlRef, polylines: [], editingActive: polylineEditingActive, }); @@ -63,6 +65,10 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { setVerticalScale(value); } + function handleDrag(info: PickingInfo): void { + onDrag(info); + } + let adjustedLayers = [...props.layers]; if (!gridVisible) { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); @@ -75,19 +81,19 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { - {cursorIcon} setCameraPositionSetByAction(null)} + onDrag={handleDrag} onMouseEvent={handleMouseEvent} - userCameraInteractionActive={!disableCameraInteraction} layers={adjustedLayers} verticalScale={verticalScale} scale={{ @@ -106,6 +112,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { }} triggerHome={triggerHomeCounter} pickingRadius={5} + getCursor={getCursor} > {props.viewportAnnotations} diff --git a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx index 2dc9eea1c..8ce3d05be 100644 --- a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx @@ -10,12 +10,13 @@ export type ToolbarProps = { verticalScale: number; onFitInView: () => void; onGridVisibilityChange: (visible: boolean) => void; - onEditPolyline: () => void; + onToggleEditPolyline: () => void; onVerticalScaleChange(value: number): void; }; export function Toolbar(props: ToolbarProps): React.ReactNode { const [gridVisible, setGridVisible] = React.useState(false); + const [polylineEditingActive, setPolylineEditingActive] = React.useState(false); function handleFitInViewClick() { props.onFitInView(); @@ -43,9 +44,9 @@ export function Toolbar(props: ToolbarProps): React.ReactNode { {gridVisible ? : } - + d, + getFillColor: [255, 255, 255, 1], + getLineColor: [0, 0, 0, 0], + getLineWidth: 1, + extruded: false, + radius: 40, + radiusUnits: "pixels", + pickable: true, + parameters: { + depthTest: false, + }, }) ); diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts new file mode 100644 index 000000000..7beeeb01a --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts @@ -0,0 +1,57 @@ +import { CompositeLayer, Layer, PickingInfo } from "@deck.gl/core"; +import { PathLayer } from "@deck.gl/layers"; + +import { Polyline } from "../types"; + +export type PolylinesLayerProps = { + id: string; + polylines: Polyline[]; +}; + +export class PolylinesLayer extends CompositeLayer { + static layerName: string = "PolylinesLayer"; + + // @ts-expect-error + state!: { + hoveredPolylineIndex: number | null; + }; + + onHover(info: PickingInfo, pickingEvent: any): boolean { + if (!info.object) { + return false; + } + + const hoveredPolylineIndex = this.props.polylines[info.index]; + this.setState({ hoveredPolylineIndex }); + + return false; + } + + renderLayers(): Layer[] { + const { hoveredPolylineIndex } = this.state; + + const layers: Layer[] = [ + new PathLayer({ + id: `polylines`, + data: this.props.polylines, + getPath: (d) => d.polyline, + getColor: (d: Polyline) => d.color, + getWidth: 2, + }), + ]; + + if (hoveredPolylineIndex !== null) { + layers.push( + new PathLayer({ + id: `hovered`, + data: [this.props.polylines[hoveredPolylineIndex]], + getPath: (d) => d.polyline, + getColor: [255, 255, 255, 255], + getWidth: 4, + }) + ); + } + + return layers; + } +} diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx index a0bcbc6ab..3218792be 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx @@ -1,31 +1,40 @@ import React from "react"; +import addPathIcon from "@assets/add_path.svg"; +import continuePathIcon from "@assets/continue_path.svg"; +import removePathIcon from "@assets/remove_path.svg"; +import setPathPointIcon from "@assets/set_path_point.svg"; import { Layer, PickingInfo } from "@deck.gl/core"; -import { Add, Polyline as PolylineIcon, Remove } from "@mui/icons-material"; +import { DeckGLProps, DeckGLRef } from "@deck.gl/react"; import { MapMouseEvent } from "@webviz/subsurface-viewer"; import { isEqual } from "lodash"; +import { v4 } from "uuid"; import { EditablePolyline, EditablePolylineLayer, isEditablePolylineLayerPickingInfo, } from "./deckGlLayers/EditablePolylineLayer"; +import { PolylinesLayer } from "./deckGlLayers/PolylinesLayer"; import { Polyline } from "./types"; export type UseEditablePolylinesProps = { + deckGlRef: React.MutableRefObject; editingActive: boolean; polylines: Polyline[]; }; export type UseEditablePolylinesReturnType = { layers: Layer[]; + onDrag: (info: PickingInfo) => boolean; onMouseEvent: (event: MapMouseEvent) => void; - cursorIcon: React.ReactNode | null; disableCameraInteraction: boolean; + getCursor: DeckGLProps["getCursor"]; }; enum CursorIcon { + NONE = "none", ADD_POINT = "add-point", REMOVE_POINT = "remove-point", CONTINUE_FROM_POINT = "continue-from-point", @@ -36,11 +45,14 @@ enum PathAppendLocation { END = "end", } +const PATH_ADD_POINT_ICON = ""; + export function useEditablePolylines(props: UseEditablePolylinesProps): UseEditablePolylinesReturnType { const [hoverPoint, setHoverPoint] = React.useState(null); const [activePolyline, setActivePolyline] = React.useState(null); const [polylines, setPolylines] = React.useState(props.polylines); const [prevPolylines, setPrevPolylines] = React.useState(props.polylines); + const [prevEditingActive, setPrevEditingActive] = React.useState(props.editingActive); const [cursorIcon, setCursorIcon] = React.useState(null); const [cursorPosition, setCursorPosition] = React.useState(null); const [appendLocation, setAppendLocation] = React.useState(PathAppendLocation.END); @@ -51,6 +63,21 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita setPrevPolylines(props.polylines); } + if (!isEqual(props.editingActive, prevEditingActive)) { + setPrevEditingActive(props.editingActive); + if (activePolyline && !props.editingActive) { + setPolylines((prev) => [ + ...prev, + { + id: v4(), + color: [230, 136, 21, 255], + polyline: activePolyline.path, + }, + ]); + } + setActivePolyline(null); + } + React.useEffect(function onMount() { function onKeyUp(event: KeyboardEvent) { if (event.key === "Escape") { @@ -70,6 +97,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita if (event.type === "hover") { if (!event.x || !event.y) { setHoverPoint(null); + setCursorIcon(CursorIcon.NONE); return; } @@ -78,6 +106,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita if (!firstLayerInfos) { setHoverPoint(null); setCursorPosition(null); + setCursorIcon(CursorIcon.NONE); return; } @@ -116,6 +145,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita } } else { setCursorPosition(null); + setCursorIcon(CursorIcon.NONE); } if (firstLayerInfos && firstLayerInfos.coordinate) { @@ -234,30 +264,84 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita [props.editingActive, activePolyline, appendLocation] ); - const onDragStart = React.useCallback(function onDragStart(info: PickingInfo) { - if (!isEditablePolylineLayerPickingInfo(info)) { - return true; - } + const disablePanning = React.useCallback( + function disablePanning() { + if (props.deckGlRef.current?.deck) { + props.deckGlRef.current.deck.setProps({ + controller: { + dragRotate: false, + dragPan: false, + }, + }); + } + }, + [props.deckGlRef] + ); - if (info.editableEntity?.type === "point") { - setDraggedPointIndex(info.editableEntity.index); - } + const enablePanning = React.useCallback( + function enablePanning() { + if (props.deckGlRef.current?.deck) { + props.deckGlRef.current.deck.setProps({ + controller: { + dragRotate: true, + dragPan: true, + }, + }); + } + }, + [props.deckGlRef] + ); - return false; - }, []); + const onDragStart = React.useCallback( + function onDragStart(info: PickingInfo) { + if (!isEditablePolylineLayerPickingInfo(info)) { + return false; + } + + if (info.editableEntity?.type === "point") { + setDraggedPointIndex(info.editableEntity.index); + disablePanning(); + } + + return true; + }, + [disablePanning] + ); const onDrag = React.useCallback( function onDrag(info: PickingInfo) { if (draggedPointIndex === null || !activePolyline || !info.coordinate) { - return true; + return false; + } + + if (!info.viewport) { + return false; + } + + if (!props.deckGlRef.current) { + return false; + } + + const layers = props.deckGlRef.current.pickMultipleObjects({ + x: info.x, + y: info.y, + radius: 10, + depth: 2, + unproject3D: true, + }); + + if (!layers.length) { + return false; + } + + const firstLayerInfos = layers[0]; + + if (!firstLayerInfos || !firstLayerInfos.coordinate) { + return false; } const newPath = [...activePolyline.path]; - newPath[draggedPointIndex] = [ - info.coordinate[0], - info.coordinate[1], - activePolyline.path[draggedPointIndex][2], - ]; + newPath[draggedPointIndex] = [...firstLayerInfos.coordinate]; setActivePolyline({ ...activePolyline, @@ -269,11 +353,20 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita [draggedPointIndex, activePolyline] ); - const onDragEnd = React.useCallback(function onDragEnd(info: PickingInfo) { - setDraggedPointIndex(null); - }, []); + const onDragEnd = React.useCallback( + function onDragEnd(info: PickingInfo) { + setDraggedPointIndex(null); + enablePanning(); + }, + [enablePanning] + ); - const layers: Layer[] = []; + const layers: Layer[] = [ + new PolylinesLayer({ + id: "polylines-layer", + polylines, + }), + ]; if (activePolyline) { layers.push( @@ -283,36 +376,42 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita mouseHoverPoint: hoverPoint ?? undefined, onDragStart, onDragEnd, - onDrag, }) ); } - const cursorIconElement = - cursorIcon && cursorPosition ? ( -
- {cursorIcon === CursorIcon.CONTINUE_FROM_POINT ? ( - - ) : cursorIcon === CursorIcon.ADD_POINT ? ( - - ) : cursorIcon === CursorIcon.REMOVE_POINT ? ( - - ) : null} -
- ) : null; + const getCursor = React.useCallback( + function getCursor(cursorState: Parameters>[0]): string { + if (cursorState.isDragging) { + return "grabbing"; + } + + if (cursorIcon === CursorIcon.CONTINUE_FROM_POINT) { + return `url(${continuePathIcon}), crosshair`; + } + + if (cursorIcon === CursorIcon.ADD_POINT) { + return `url(${addPathIcon}), crosshair`; + } + + if (cursorIcon === CursorIcon.REMOVE_POINT) { + return `url(${removePathIcon}), crosshair`; + } + + if (activePolyline) { + return `url(${setPathPointIcon}), crosshair`; + } + + return "default"; + }, + [cursorIcon, activePolyline] + ); return { layers, onMouseEvent, - cursorIcon: cursorIconElement, + onDrag, + getCursor, disableCameraInteraction: draggedPointIndex !== null, }; } From d1979fd2f5f9548f5790789941362b3d31b62ad4 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 20 Jan 2025 21:57:51 +0100 Subject: [PATCH 09/97] wip --- .../view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts | 2 +- .../subsurfaceViewerWithCameraState.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts index 7beeeb01a..6a8ea7e9f 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts @@ -40,7 +40,7 @@ export class PolylinesLayer extends CompositeLayer { }), ]; - if (hoveredPolylineIndex !== null) { + if (hoveredPolylineIndex !== null && this.props.polylines[hoveredPolylineIndex]) { layers.push( new PathLayer({ id: `hovered`, diff --git a/frontend/src/modules/_shared/components/SubsurfaceViewerWithCameraState/subsurfaceViewerWithCameraState.tsx b/frontend/src/modules/_shared/components/SubsurfaceViewerWithCameraState/subsurfaceViewerWithCameraState.tsx index fe6a1b522..03af3816f 100644 --- a/frontend/src/modules/_shared/components/SubsurfaceViewerWithCameraState/subsurfaceViewerWithCameraState.tsx +++ b/frontend/src/modules/_shared/components/SubsurfaceViewerWithCameraState/subsurfaceViewerWithCameraState.tsx @@ -39,7 +39,6 @@ export function SubsurfaceViewerWithCameraState(props: SubsurfaceViewerWithCamer const handleCameraChange = React.useCallback( function handleCameraChange(viewport: ViewStateType): void { if (props.userCameraInteractionActive || props.userCameraInteractionActive === undefined) { - console.debug("Camera position changed", viewport); setCameraPosition(viewport); } }, From 401ed95a3ccf4ffb82bf6f2653864ac27a13ddf6 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 20 Jan 2025 21:59:02 +0100 Subject: [PATCH 10/97] wip --- .../editablePolylines/deckGlLayers/EditablePolylineLayer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts index edc160a7f..39d087c41 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts @@ -43,7 +43,7 @@ export class EditablePolylineLayer extends CompositeLayer Date: Tue, 21 Jan 2025 17:13:58 +0100 Subject: [PATCH 11/97] wip --- frontend/package-lock.json | 37 ++- frontend/package.json | 2 + frontend/src/assets/add_path.svg | 188 +++--------- frontend/src/assets/continue_path.svg | 190 +++--------- frontend/src/assets/path.svg | 20 ++ frontend/src/assets/remove_path.svg | 197 +++---------- frontend/src/assets/set_path_point.svg | 190 +++--------- .../RealizationGridLayer.ts | 4 +- .../RealizationGridSettingsContext.ts | 8 +- .../RealizationGridLayer/types.ts | 2 +- .../RealizationGridLayer.ts | 17 +- .../RealizationGridSettingsContext.ts | 52 +++- .../RealizationGridLayer/types.ts | 4 +- .../view/components/LayersWrapper.tsx | 1 + .../view/components/ReadoutWrapper.tsx | 22 +- .../3DViewerNew/view/components/Toolbar.tsx | 23 +- .../deckGlLayers/AnimatedPathLayer.ts | 28 +- .../deckGlLayers/EditablePolylineLayer.ts | 25 +- .../deckGlLayers/PolylinesLayer.ts | 141 +++++++-- .../editablePolylinesHook.tsx | 279 ++++++++++++------ .../view/hooks/editablePolylines/types.ts | 11 +- .../implementations/GridLayerISetting.tsx | 101 +++++++ .../implementations/GridLayerJSetting.tsx | 101 +++++++ ...LayerSetting.tsx => GridLayerKSetting.tsx} | 8 +- .../LayerFramework/settings/settingsTypes.ts | 4 +- 25 files changed, 858 insertions(+), 797 deletions(-) create mode 100644 frontend/src/assets/path.svg create mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerISetting.tsx create mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJSetting.tsx rename frontend/src/modules/_shared/LayerFramework/settings/implementations/{GridLayerSetting.tsx => GridLayerKSetting.tsx} (93%) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4a4ada631..739dc7d20 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,6 +12,8 @@ "@equinor/esv-intersection": "^3.0.10", "@headlessui/react": "^1.7.8", "@hey-api/client-axios": "^0.4.0", + "@luma.gl/engine": "^9.0.28", + "@luma.gl/webgl": "^9.0.28", "@mui/base": "^5.0.0-beta.3", "@mui/icons-material": "^5.14.9", "@tanstack/query-core": "^5.62.16", @@ -2213,9 +2215,9 @@ } }, "node_modules/@luma.gl/constants": { - "version": "9.0.27", - "resolved": "https://registry.npmjs.org/@luma.gl/constants/-/constants-9.0.27.tgz", - "integrity": "sha512-NBkMim3u0xt4UDe4e69L6E/pq5XNxfX60GrggJDzfilVRfIbx5XwKhBXTyNjjtNEk4oc6uYLHWd/05jGRHcfLg==" + "version": "9.0.28", + "resolved": "https://registry.npmjs.org/@luma.gl/constants/-/constants-9.0.28.tgz", + "integrity": "sha512-mw3YwYfCbpCa8y28nNZGaJemprSkqthsunz0V79qpnMrFD3pOMIh+rw/hjQuqVW9ffmSXx+xY2bI8FT1YC8f7A==" }, "node_modules/@luma.gl/core": { "version": "9.0.27", @@ -2230,11 +2232,11 @@ } }, "node_modules/@luma.gl/engine": { - "version": "9.0.27", - "resolved": "https://registry.npmjs.org/@luma.gl/engine/-/engine-9.0.27.tgz", - "integrity": "sha512-O4e7RbIjBJX5WLs8HJLjpccYEkcans4pz8+TI8Y7BO7gDq9ZbEASbVd5CT53jFLfTjnRuqAOpElfaXwQ/B7oWg==", + "version": "9.0.28", + "resolved": "https://registry.npmjs.org/@luma.gl/engine/-/engine-9.0.28.tgz", + "integrity": "sha512-EidAG/1Sgq0OeoyrebkcrC5TMvfK8ycIpSAjIRdi8hjwpHIoHFj5P3MWeApTZ1HV0DroxChzrxRCHYylI8svPQ==", "dependencies": { - "@luma.gl/shadertools": "9.0.27", + "@luma.gl/shadertools": "9.0.28", "@math.gl/core": "^4.0.0", "@probe.gl/log": "^4.0.2", "@probe.gl/stats": "^4.0.2" @@ -2243,6 +2245,19 @@ "@luma.gl/core": "^9.0.0" } }, + "node_modules/@luma.gl/engine/node_modules/@luma.gl/shadertools": { + "version": "9.0.28", + "resolved": "https://registry.npmjs.org/@luma.gl/shadertools/-/shadertools-9.0.28.tgz", + "integrity": "sha512-FFh9udQTcPOTGpMKjYbCqZ7M6SLiaER93afCpQoYT6i36bPjTX4WX51ijFxA1ABJyvsZAo4BLR54tF++jNxyEQ==", + "dependencies": { + "@math.gl/core": "^4.0.0", + "@math.gl/types": "^4.0.0", + "wgsl_reflect": "^1.0.1" + }, + "peerDependencies": { + "@luma.gl/core": "^9.0.0" + } + }, "node_modules/@luma.gl/gltf": { "version": "9.0.27", "resolved": "https://registry.npmjs.org/@luma.gl/gltf/-/gltf-9.0.27.tgz", @@ -2272,11 +2287,11 @@ } }, "node_modules/@luma.gl/webgl": { - "version": "9.0.27", - "resolved": "https://registry.npmjs.org/@luma.gl/webgl/-/webgl-9.0.27.tgz", - "integrity": "sha512-GOzOiDfTFgT4If1XSeCqXswKrgXVwTyuf/1W21Vv7fs5inub5p3LISmZglrt/RcdaGyXQQ5zEqf/+x67dGTeYw==", + "version": "9.0.28", + "resolved": "https://registry.npmjs.org/@luma.gl/webgl/-/webgl-9.0.28.tgz", + "integrity": "sha512-CvEUlKkppNCdmr/Ilyb6ZzOSDaLDmhOIfR1wBDj9Gf8QJB7yZkrzapstHKkyvmKuf480ATamjlSABkYUD0CguQ==", "dependencies": { - "@luma.gl/constants": "9.0.27", + "@luma.gl/constants": "9.0.28", "@probe.gl/env": "^4.0.2" }, "peerDependencies": { diff --git a/frontend/package.json b/frontend/package.json index 79736ee08..62da53bb5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -23,6 +23,8 @@ "@equinor/esv-intersection": "^3.0.10", "@headlessui/react": "^1.7.8", "@hey-api/client-axios": "^0.4.0", + "@luma.gl/engine": "^9.0.28", + "@luma.gl/webgl": "^9.0.28", "@mui/base": "^5.0.0-beta.3", "@mui/icons-material": "^5.14.9", "@tanstack/query-core": "^5.62.16", diff --git a/frontend/src/assets/add_path.svg b/frontend/src/assets/add_path.svg index 4d70f9fee..eaf50c56a 100644 --- a/frontend/src/assets/add_path.svg +++ b/frontend/src/assets/add_path.svg @@ -1,155 +1,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/continue_path.svg b/frontend/src/assets/continue_path.svg index 52d9d13cf..dc7fe1d08 100644 --- a/frontend/src/assets/continue_path.svg +++ b/frontend/src/assets/continue_path.svg @@ -1,157 +1,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/path.svg b/frontend/src/assets/path.svg new file mode 100644 index 000000000..2e17d9e11 --- /dev/null +++ b/frontend/src/assets/path.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/remove_path.svg b/frontend/src/assets/remove_path.svg index 57334da7f..0e002bb8a 100644 --- a/frontend/src/assets/remove_path.svg +++ b/frontend/src/assets/remove_path.svg @@ -1,156 +1,41 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/set_path_point.svg b/frontend/src/assets/set_path_point.svg index b3ef1a84d..c042b4664 100644 --- a/frontend/src/assets/set_path_point.svg +++ b/frontend/src/assets/set_path_point.svg @@ -1,154 +1,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts index 1020308d7..d65433fe3 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -113,11 +113,11 @@ export class RealizationGridLayer if (timeOrInterval === "NO_TIME") { timeOrInterval = null; } - let availableDimensions = settings[SettingType.GRID_LAYER].getDelegate().getAvailableValues(); + let availableDimensions = settings[SettingType.GRID_LAYER_K].getDelegate().getAvailableValues(); if (!availableDimensions.length || availableDimensions[0] === null) { availableDimensions = [0, 0, 0]; } - const layerIndex = settings[SettingType.GRID_LAYER].getDelegate().getValue(); + const layerIndex = settings[SettingType.GRID_LAYER_K].getDelegate().getValue(); const iMin = 0; const iMax = availableDimensions[0] || 0; const jMin = 0; diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts index 88bb973b6..7ea7e872b 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts @@ -4,7 +4,7 @@ import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerMan import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; import { GridAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting"; -import { GridLayerSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerSetting"; +import { GridLayerKSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKSetting"; import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; @@ -25,7 +25,7 @@ export class RealizationGridSettingsContext implements SettingsContext { + availableSettingsUpdater(SettingType.GRID_LAYER_K, ({ getLocalSetting, getHelperDependency }) => { const gridName = getLocalSetting(SettingType.GRID_NAME); const data = getHelperDependency(realizationGridDataDep); diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts index f5186f280..51a41b25e 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts @@ -6,7 +6,7 @@ export type RealizationGridSettings = { [SettingType.REALIZATION]: number | null; [SettingType.GRID_ATTRIBUTE]: string | null; [SettingType.GRID_NAME]: string | null; - [SettingType.GRID_LAYER]: number | null; + [SettingType.GRID_LAYER_K]: number | null; [SettingType.TIME_OR_INTERVAL]: string | null; [SettingType.SHOW_GRID_LINES]: boolean; }; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts index 1020308d7..b9540bf1d 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -113,17 +113,22 @@ export class RealizationGridLayer if (timeOrInterval === "NO_TIME") { timeOrInterval = null; } - let availableDimensions = settings[SettingType.GRID_LAYER].getDelegate().getAvailableValues(); + let availableDimensions = settings[SettingType.GRID_LAYER_K].getDelegate().getAvailableValues(); if (!availableDimensions.length || availableDimensions[0] === null) { availableDimensions = [0, 0, 0]; } - const layerIndex = settings[SettingType.GRID_LAYER].getDelegate().getValue(); - const iMin = 0; + const layerIIndex = settings[SettingType.GRID_LAYER_I].getDelegate().getValue(); + const iMin = layerIIndex || 0; const iMax = availableDimensions[0] || 0; - const jMin = 0; + + const layerJIndex = settings[SettingType.GRID_LAYER_J].getDelegate().getValue(); + const jMin = layerJIndex || 0; const jMax = availableDimensions[1] || 0; - const kMin = layerIndex || 0; - const kMax = layerIndex || 0; + + const layerKIndex = settings[SettingType.GRID_LAYER_K].getDelegate().getValue(); + const kMin = layerKIndex || 0; + const kMax = availableDimensions[2] || 0; + const queryKey = [ "gridParameter", ensembleIdent, diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts index 88bb973b6..3013a27cc 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts @@ -4,7 +4,9 @@ import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerMan import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; import { GridAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting"; -import { GridLayerSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerSetting"; +import { GridLayerISetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerISetting"; +import { GridLayerJSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerJSetting"; +import { GridLayerKSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKSetting"; import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; @@ -25,7 +27,9 @@ export class RealizationGridSettingsContext implements SettingsContext { + availableSettingsUpdater(SettingType.GRID_LAYER_I, ({ getLocalSetting, getHelperDependency }) => { + const gridName = getLocalSetting(SettingType.GRID_NAME); + const data = getHelperDependency(realizationGridDataDep); + + if (!gridName || !data) { + return []; + } + + const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; + const availableGridLayers: number[] = []; + if (gridDimensions) { + availableGridLayers.push(gridDimensions.i_count); + availableGridLayers.push(gridDimensions.j_count); + availableGridLayers.push(gridDimensions.k_count); + } + + return availableGridLayers; + }); + + availableSettingsUpdater(SettingType.GRID_LAYER_J, ({ getLocalSetting, getHelperDependency }) => { + const gridName = getLocalSetting(SettingType.GRID_NAME); + const data = getHelperDependency(realizationGridDataDep); + + if (!gridName || !data) { + return []; + } + + const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; + const availableGridLayers: number[] = []; + if (gridDimensions) { + availableGridLayers.push(gridDimensions.i_count); + availableGridLayers.push(gridDimensions.j_count); + availableGridLayers.push(gridDimensions.k_count); + } + + return availableGridLayers; + }); + + availableSettingsUpdater(SettingType.GRID_LAYER_K, ({ getLocalSetting, getHelperDependency }) => { const gridName = getLocalSetting(SettingType.GRID_NAME); const data = getHelperDependency(realizationGridDataDep); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts index f5186f280..6ba7a6f00 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts @@ -6,7 +6,9 @@ export type RealizationGridSettings = { [SettingType.REALIZATION]: number | null; [SettingType.GRID_ATTRIBUTE]: string | null; [SettingType.GRID_NAME]: string | null; - [SettingType.GRID_LAYER]: number | null; + [SettingType.GRID_LAYER_I]: number | null; + [SettingType.GRID_LAYER_J]: number | null; + [SettingType.GRID_LAYER_K]: number | null; [SettingType.TIME_OR_INTERVAL]: string | null; [SettingType.SHOW_GRID_LINES]: boolean; }; diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 3839b4304..c49875dfe 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -80,6 +80,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { "placeholder", "axes-layer", "editable-polylines-layer", + "polylines-layer", "hover-point-layer", ], }); diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index 874bb7285..d5ceafefc 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -2,6 +2,8 @@ import React from "react"; import { Layer as DeckGlLayer, PickingInfo } from "@deck.gl/core"; import { DeckGLRef } from "@deck.gl/react"; +import { Menu } from "@lib/components/Menu"; +import { MenuItem } from "@lib/components/MenuItem"; import { SubsurfaceViewerWithCameraState } from "@modules/_shared/components/SubsurfaceViewerWithCameraState"; import { BoundingBox3D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; @@ -29,14 +31,17 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const [verticalScale, setVerticalScale] = React.useState(1); const [polylineEditingActive, setPolylineEditingActive] = React.useState(false); - const { onMouseEvent, layers, onDrag, getCursor } = useEditablePolylines({ + const onEditingDone = React.useCallback(function onEditingDone() { + setPolylineEditingActive(false); + }, []); + + const { onMouseEvent, layers, onDrag, getCursor, cursorPosition, contextMenuItems } = useEditablePolylines({ deckGlRef, polylines: [], editingActive: polylineEditingActive, + onEditingDone, }); - // deckGlRef.current?.deck.setProps({...deckGlRef.props}) - function handleFitInViewClick() { setTriggerHomeCounter((prev) => prev + 1); } @@ -84,7 +89,18 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { onToggleEditPolyline={handleEditPolylines} onVerticalScaleChange={handleVerticalScaleChange} verticalScale={verticalScale} + polylineEditingActive={polylineEditingActive} /> + {cursorPosition && contextMenuItems.length && ( + + {contextMenuItems.map((item, index) => ( + + {item.icon} + {item.label} + + ))} + + )} void; onGridVisibilityChange: (visible: boolean) => void; onToggleEditPolyline: () => void; @@ -16,7 +28,6 @@ export type ToolbarProps = { export function Toolbar(props: ToolbarProps): React.ReactNode { const [gridVisible, setGridVisible] = React.useState(false); - const [polylineEditingActive, setPolylineEditingActive] = React.useState(false); function handleFitInViewClick() { props.onFitInView(); @@ -44,8 +55,12 @@ export function Toolbar(props: ToolbarProps): React.ReactNode { {gridVisible ? : } - - + + | null = null; initializeState() { super.initializeState(); @@ -13,6 +14,9 @@ export class AnimatedPathLayer extends PathLayer { updateState(params: UpdateParameters): void { super.updateState(params); + if (this._requestId) { + cancelAnimationFrame(this._requestId); + } this.animate(); } @@ -20,20 +24,28 @@ export class AnimatedPathLayer extends PathLayer { this._dashStart = (Date.now() / 50) % 1000; this.setNeedsRedraw(); - requestAnimationFrame(() => this.animate()); + this._requestId = requestAnimationFrame(() => this.animate()); } getShaders() { const shaders = super.getShaders(); - shaders.inject["vs:#decl"] += `\ - uniform float dashStart;`; - shaders.inject["vs:#main-end"] += `\ - vDashOffset += dashStart;`; - return shaders; + return { + ...shaders, + inject: { + ...shaders.inject, + "vs:#decl": + shaders.inject["vs:#decl"] + + `\ + uniform float dashStart;`, + "vs:#main-end": + shaders.inject["vs:#main-end"] + + `\ + vDashOffset += dashStart;`, + }, + }; } draw({ uniforms }: Record) { - uniforms.dashStart = this._dashStart || 0; - super.draw({ uniforms }); + super.draw({ uniforms: { ...uniforms, dashStart: this._dashStart } }); } } diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts index 39d087c41..56972b03c 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts @@ -1,18 +1,15 @@ -import { CompositeLayer, GetPickingInfoParams, Layer, LayerContext, PickingInfo } from "@deck.gl/core"; +import { CompositeLayer, GetPickingInfoParams, Layer, PickingInfo } from "@deck.gl/core"; import { PathStyleExtension } from "@deck.gl/extensions"; import { ColumnLayer, LineLayer, PathLayer } from "@deck.gl/layers"; import { AnimatedPathLayer } from "./AnimatedPathLayer"; +import { Polyline } from "../types"; + export type EditablePolylineLayerProps = { id: string; - polyline: EditablePolyline; + polyline: Polyline; mouseHoverPoint?: number[]; -}; - -export type EditablePolyline = { - color: [number, number, number, number]; - path: number[][]; referencePathPointIndex?: number; }; @@ -34,7 +31,7 @@ export function isEditablePolylineLayerPickingInfo(info: PickingInfo): info is E export class EditablePolylineLayer extends CompositeLayer { static layerName: string = "EditablePolylineLayer"; - // @ts-expect-error + // @ts-expect-error - deck.gl types are wrong state!: { hoveredEntity: { layer: "line" | "point"; @@ -91,7 +88,7 @@ export class EditablePolylineLayer extends CompositeLayer[] = []; @@ -138,7 +135,7 @@ export class EditablePolylineLayer extends CompositeLayer d, getFillColor: (d, context) => { - if (context.index === polyline.referencePathPointIndex) { + if (context.index === referencePathPointIndex) { return [230, 136, 21, 255]; } return [255, 255, 255, 255]; @@ -163,8 +160,8 @@ export class EditablePolylineLayer extends CompositeLayer d.from, getTargetPosition: (d) => d.to, getColor: [230, 136, 21, 100], diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts index 6a8ea7e9f..c6ea053b9 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts @@ -1,57 +1,162 @@ -import { CompositeLayer, Layer, PickingInfo } from "@deck.gl/core"; -import { PathLayer } from "@deck.gl/layers"; +import { CompositeLayer, GetPickingInfoParams, Layer, PickingInfo } from "@deck.gl/core"; +import { PathLayer, TextLayer } from "@deck.gl/layers"; import { Polyline } from "../types"; export type PolylinesLayerProps = { id: string; polylines: Polyline[]; + selectedPolylineId?: string; }; +export type PolylinesLayerPickingInfo = PickingInfo & { + polylineId?: string; +}; + +export function isPolylinesLayerPickingInfo(info: PickingInfo): info is PolylinesLayerPickingInfo { + return Object.keys(info).includes("polylineId"); +} + export class PolylinesLayer extends CompositeLayer { static layerName: string = "PolylinesLayer"; - // @ts-expect-error + // @ts-expect-error - deck.gl types are wrong state!: { hoveredPolylineIndex: number | null; }; - onHover(info: PickingInfo, pickingEvent: any): boolean { - if (!info.object) { + onHover(info: PickingInfo): boolean { + if (info.index === undefined) { return false; } - const hoveredPolylineIndex = this.props.polylines[info.index]; + const hoveredPolylineIndex = info.index; this.setState({ hoveredPolylineIndex }); return false; } + getPickingInfo({ info }: GetPickingInfoParams): PolylinesLayerPickingInfo { + if (info && info.sourceLayer && info.object !== undefined) { + return { + ...info, + polylineId: info.object.id, + }; + } + + return info; + } + renderLayers(): Layer[] { const { hoveredPolylineIndex } = this.state; - const layers: Layer[] = [ - new PathLayer({ - id: `polylines`, - data: this.props.polylines, - getPath: (d) => d.polyline, - getColor: (d: Polyline) => d.color, - getWidth: 2, - }), - ]; + const layers: Layer[] = []; + + if (this.props.selectedPolylineId) { + const selectedPolylineIndex = this.props.polylines.findIndex((p) => p.id === this.props.selectedPolylineId); + if (selectedPolylineIndex !== -1) { + layers.push( + new PathLayer({ + id: `selected`, + data: [this.props.polylines[selectedPolylineIndex]], + getPath: (d) => d.path, + getColor: (d: Polyline) => [d.color[0], d.color[1], d.color[2], 200], + getWidth: 30, + widthUnits: "meters", + parameters: { + depthTest: false, + }, + }) + ); + } + } if (hoveredPolylineIndex !== null && this.props.polylines[hoveredPolylineIndex]) { layers.push( new PathLayer({ id: `hovered`, data: [this.props.polylines[hoveredPolylineIndex]], - getPath: (d) => d.polyline, - getColor: [255, 255, 255, 255], - getWidth: 4, + getPath: (d) => d.path, + getColor: (d: Polyline) => [d.color[0], d.color[1], d.color[2], 100], + getWidth: 30, + widthUnits: "meters", + parameters: { + depthTest: false, + }, }) ); } + const polylineLabels: { label: string; position: number[]; angle: number; color: number[] }[] = []; + for (const polyline of this.props.polylines) { + for (let i = 0; i < polyline.path.length - 1; i++) { + const vector = [ + polyline.path[i + 1][0] - polyline.path[i][0], + polyline.path[i + 1][1] - polyline.path[i][1], + polyline.path[i + 1][2] - polyline.path[i][2], + ]; + const length = Math.sqrt(vector[0] ** 2 + vector[1] ** 2); + const unitVector = [vector[0] / length, vector[1] / length, vector[2] / length]; + const normalVector = [-unitVector[1], unitVector[0], 0]; + let angle = Math.atan2(unitVector[1], unitVector[0]) * (180 / Math.PI); + if (angle > 90 || angle < -90) { + angle += 180; + } + polylineLabels.push({ + label: polyline.name, + position: [ + polyline.path[i][0] + (unitVector[0] * length) / 2 + (normalVector[0] * 100) / 2, + polyline.path[i][1] + (unitVector[1] * length) / 2 + (normalVector[1] * 100) / 2, + polyline.path[i][2] + (unitVector[2] * length) / 2 + (normalVector[2] * 100) / 2, + ], + angle, + color: polyline.color, + }); + } + } + + layers.push( + new PathLayer({ + id: `polylines`, + data: this.props.polylines, + getPath: (d) => d.path, + getColor: (d: Polyline) => d.color, + getWidth: 10, + widthUnits: "meters", + pickable: false, + parameters: { + depthTest: false, + }, + }), + new TextLayer({ + id: `polylines-labels`, + data: polylineLabels, + getPosition: (d) => d.position, + getText: (d) => d.label, + getSize: 12, + sizeUnits: "meters", + sizeMinPixels: 12, + getAngle: (d) => d.angle, + getColor: (d: Polyline) => d.color, + parameters: { + depthTest: false, + }, + billboard: false, + }), + new PathLayer({ + id: `polylines-hoverable`, + data: this.props.polylines, + getPath: (d) => d.path, + getColor: (d: Polyline) => [d.color[0], d.color[1], d.color[2], 1], + getWidth: 50, + widthUnits: "meters", + pickable: true, + parameters: { + depthTest: false, + }, + }) + ); + return layers; } } diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx index 3218792be..4a2ea1972 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx @@ -6,23 +6,21 @@ import removePathIcon from "@assets/remove_path.svg"; import setPathPointIcon from "@assets/set_path_point.svg"; import { Layer, PickingInfo } from "@deck.gl/core"; import { DeckGLProps, DeckGLRef } from "@deck.gl/react"; +import { Edit, Remove } from "@mui/icons-material"; import { MapMouseEvent } from "@webviz/subsurface-viewer"; import { isEqual } from "lodash"; import { v4 } from "uuid"; -import { - EditablePolyline, - EditablePolylineLayer, - isEditablePolylineLayerPickingInfo, -} from "./deckGlLayers/EditablePolylineLayer"; -import { PolylinesLayer } from "./deckGlLayers/PolylinesLayer"; -import { Polyline } from "./types"; +import { EditablePolylineLayer, isEditablePolylineLayerPickingInfo } from "./deckGlLayers/EditablePolylineLayer"; +import { PolylinesLayer, isPolylinesLayerPickingInfo } from "./deckGlLayers/PolylinesLayer"; +import { ContextMenuItem, Polyline } from "./types"; export type UseEditablePolylinesProps = { deckGlRef: React.MutableRefObject; editingActive: boolean; polylines: Polyline[]; + onEditingDone?: (polylines: Polyline[]) => void; }; export type UseEditablePolylinesReturnType = { @@ -31,10 +29,13 @@ export type UseEditablePolylinesReturnType = { onMouseEvent: (event: MapMouseEvent) => void; disableCameraInteraction: boolean; getCursor: DeckGLProps["getCursor"]; + cursorPosition: number[] | null; + contextMenuItems: ContextMenuItem[]; }; enum CursorIcon { NONE = "none", + POINTER = "pointer", ADD_POINT = "add-point", REMOVE_POINT = "remove-point", CONTINUE_FROM_POINT = "continue-from-point", @@ -45,11 +46,11 @@ enum PathAppendLocation { END = "end", } -const PATH_ADD_POINT_ICON = ""; - export function useEditablePolylines(props: UseEditablePolylinesProps): UseEditablePolylinesReturnType { + const { onEditingDone } = props; + const [hoverPoint, setHoverPoint] = React.useState(null); - const [activePolyline, setActivePolyline] = React.useState(null); + const [activePolylineId, setActivePolylineId] = React.useState(null); const [polylines, setPolylines] = React.useState(props.polylines); const [prevPolylines, setPrevPolylines] = React.useState(props.polylines); const [prevEditingActive, setPrevEditingActive] = React.useState(props.editingActive); @@ -57,6 +58,10 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita const [cursorPosition, setCursorPosition] = React.useState(null); const [appendLocation, setAppendLocation] = React.useState(PathAppendLocation.END); const [draggedPointIndex, setDraggedPointIndex] = React.useState(null); + const [contextMenuItems, setContextMenuItems] = React.useState([]); + const [selectedPolylineId, setSelectedPolylineId] = React.useState(null); + const [referencePathPointIndex, setReferencePathPointIndex] = React.useState(undefined); + const [editingActive, setEditingActive] = React.useState(props.editingActive); if (!isEqual(props.polylines, prevPolylines)) { setPolylines(props.polylines); @@ -65,23 +70,14 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita if (!isEqual(props.editingActive, prevEditingActive)) { setPrevEditingActive(props.editingActive); - if (activePolyline && !props.editingActive) { - setPolylines((prev) => [ - ...prev, - { - id: v4(), - color: [230, 136, 21, 255], - polyline: activePolyline.path, - }, - ]); - } - setActivePolyline(null); + setActivePolylineId(null); + setEditingActive(props.editingActive); } React.useEffect(function onMount() { function onKeyUp(event: KeyboardEvent) { if (event.key === "Escape") { - setActivePolyline((prev) => (!prev ? prev : { ...prev, referencePathPointIndex: undefined })); + setReferencePathPointIndex(undefined); } } @@ -94,6 +90,12 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita const onMouseEvent = React.useCallback( (event: MapMouseEvent) => { + if (draggedPointIndex !== null) { + return; + } + + const activePolyline = polylines.find((polyline) => polyline.id === activePolylineId); + if (event.type === "hover") { if (!event.x || !event.y) { setHoverPoint(null); @@ -105,7 +107,6 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita if (!firstLayerInfos) { setHoverPoint(null); - setCursorPosition(null); setCursorIcon(CursorIcon.NONE); return; } @@ -117,8 +118,6 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita editablePolylineLayerPickingInfo.viewport && firstLayerInfos.coordinate ) { - const position = editablePolylineLayerPickingInfo.viewport.project([...firstLayerInfos.coordinate]); - setCursorPosition(position); if ( editablePolylineLayerPickingInfo.editableEntity && editablePolylineLayerPickingInfo.editableEntity.type === "line" @@ -133,8 +132,8 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita ) { const index = editablePolylineLayerPickingInfo.editableEntity.index; if ( - activePolyline?.referencePathPointIndex === undefined && - (index === 0 || index === activePolyline.path.length - 1) + (index === 0 || index === activePolyline.path.length - 1) && + index !== referencePathPointIndex ) { setCursorIcon(CursorIcon.CONTINUE_FROM_POINT); return; @@ -143,26 +142,80 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita setCursorIcon(CursorIcon.REMOVE_POINT); return; } - } else { - setCursorPosition(null); - setCursorIcon(CursorIcon.NONE); } + const polylinesLayerPickingInfo = event.infos.find(isPolylinesLayerPickingInfo); + + if (polylinesLayerPickingInfo) { + setCursorIcon(CursorIcon.POINTER); + return; + } + + setCursorIcon(CursorIcon.NONE); + if (firstLayerInfos && firstLayerInfos.coordinate) { setHoverPoint([...firstLayerInfos.coordinate]); } } if (event.type === "click") { - if (!props.editingActive) { + const firstLayerInfos = event.infos[0]; + if (!firstLayerInfos || !firstLayerInfos.coordinate) { + setContextMenuItems([]); + setSelectedPolylineId(null); return; } - const firstLayerInfos = event.infos[0]; - if (!firstLayerInfos || !firstLayerInfos.coordinate) { + if (!editingActive) { + const polylinesLayerPickingInfo = event.infos.find(isPolylinesLayerPickingInfo); + if ( + !polylinesLayerPickingInfo || + !polylinesLayerPickingInfo.viewport || + !polylinesLayerPickingInfo.coordinate + ) { + setContextMenuItems([]); + setSelectedPolylineId(null); + return; + } + + const position = polylinesLayerPickingInfo.viewport.project([ + ...polylinesLayerPickingInfo.coordinate, + ]); + setCursorPosition(position); + setSelectedPolylineId(polylinesLayerPickingInfo.polylineId ?? null); + + setContextMenuItems([ + { + icon: , + label: "Edit", + onClick: () => { + setActivePolylineId(polylinesLayerPickingInfo.polylineId ?? null); + setReferencePathPointIndex(undefined); + setEditingActive(true); + setSelectedPolylineId(null); + setContextMenuItems([]); + }, + }, + { + icon: , + label: "Delete", + onClick: () => { + setPolylines((prevPolylines) => + prevPolylines.filter( + (polyline) => polyline.id !== polylinesLayerPickingInfo.polylineId + ) + ); + setContextMenuItems([]); + setSelectedPolylineId(null); + }, + }, + ]); + return; } + setContextMenuItems([]); + const editablePolylineLayerPickingInfo = event.infos.find(isEditablePolylineLayerPickingInfo); if ( activePolyline && @@ -171,46 +224,47 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita ) { // Remove point if (editablePolylineLayerPickingInfo.editableEntity.type === "point") { - if (activePolyline.referencePathPointIndex === undefined) { - let index = editablePolylineLayerPickingInfo.editableEntity.index; - if (index === 0 || index === activePolyline.path.length - 1) { - const newPolyline: EditablePolyline = { - ...activePolyline, - referencePathPointIndex: index, - }; - - setActivePolyline(newPolyline); - if (index === 0) { - setAppendLocation(PathAppendLocation.START); - } else { - setAppendLocation(PathAppendLocation.END); - } - return; + const index = editablePolylineLayerPickingInfo.editableEntity.index; + + if ( + (index === 0 || index === activePolyline.path.length - 1) && + index !== referencePathPointIndex + ) { + setReferencePathPointIndex(index); + if (index === 0) { + setAppendLocation(PathAppendLocation.START); + } else { + setAppendLocation(PathAppendLocation.END); } + return; } let newReferencePathPointIndex: number | undefined = undefined; - if (activePolyline.referencePathPointIndex !== undefined) { - newReferencePathPointIndex = Math.max(0, activePolyline.referencePathPointIndex - 1); - if ( - editablePolylineLayerPickingInfo.editableEntity.index > - activePolyline.referencePathPointIndex - ) { - newReferencePathPointIndex = activePolyline.referencePathPointIndex; + if (referencePathPointIndex !== undefined) { + newReferencePathPointIndex = Math.max(0, referencePathPointIndex - 1); + if (editablePolylineLayerPickingInfo.editableEntity.index > referencePathPointIndex) { + newReferencePathPointIndex = referencePathPointIndex; } if (activePolyline.path.length - 1 < 1) { newReferencePathPointIndex = undefined; } } - const newPolyline: EditablePolyline = { + const newPolyline: Polyline = { ...activePolyline, path: activePolyline.path.filter( (_, index) => index !== editablePolylineLayerPickingInfo.editableEntity?.index ), - referencePathPointIndex: newReferencePathPointIndex, }; - setActivePolyline(newPolyline); + setReferencePathPointIndex(newReferencePathPointIndex); + + setPolylines((prevPolylines) => + prevPolylines.map((polyline) => + polyline.id === activePolyline.id ? newPolyline : polyline + ) + ); + setCursorIcon(CursorIcon.NONE); + return; } if (editablePolylineLayerPickingInfo.editableEntity.type === "line") { @@ -220,48 +274,71 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita ]); let newReferencePathPointIndex: number | undefined = undefined; - if (activePolyline.referencePathPointIndex !== undefined) { - newReferencePathPointIndex = activePolyline.referencePathPointIndex + 1; - if ( - editablePolylineLayerPickingInfo.editableEntity.index > - activePolyline.referencePathPointIndex - ) { - newReferencePathPointIndex = activePolyline.referencePathPointIndex; + if (referencePathPointIndex !== undefined) { + newReferencePathPointIndex = referencePathPointIndex + 1; + if (editablePolylineLayerPickingInfo.editableEntity.index > referencePathPointIndex) { + newReferencePathPointIndex = referencePathPointIndex; } } - const newPolyline: EditablePolyline = { + const newPolyline: Polyline = { ...activePolyline, path: newPath, - referencePathPointIndex: newReferencePathPointIndex, }; - setActivePolyline(newPolyline); + setReferencePathPointIndex(newReferencePathPointIndex); + + setPolylines((prevPolylines) => + prevPolylines.map((polyline) => + polyline.id === activePolyline.id ? newPolyline : polyline + ) + ); + setCursorIcon(CursorIcon.NONE); return; } } if (!activePolyline) { - const newPolyline: EditablePolyline = { + const id = v4(); + const newPolyline: Polyline = { + id, + name: "New polyline", color: [230, 136, 21, 255], path: [[...firstLayerInfos.coordinate]], - referencePathPointIndex: 0, }; - setActivePolyline(newPolyline); - } else { - if (activePolyline.referencePathPointIndex === undefined) { + setPolylines([...polylines, newPolyline]); + setActivePolylineId(id); + setReferencePathPointIndex(0); + } else if (activePolyline) { + if (referencePathPointIndex === undefined) { + setActivePolylineId(null); + setEditingActive(false); + onEditingDone?.(polylines); return; } - setActivePolyline({ + const newPolyline: Polyline = { ...activePolyline, path: appendCoordinateToPath(activePolyline.path, firstLayerInfos.coordinate, appendLocation), - referencePathPointIndex: - appendLocation === PathAppendLocation.END ? activePolyline.path.length : 0, - }); + }; + setPolylines((prevPolylines) => + prevPolylines.map((polyline) => (polyline.id === activePolyline.id ? newPolyline : polyline)) + ); + + setReferencePathPointIndex( + appendLocation === PathAppendLocation.END ? activePolyline.path.length : 0 + ); } } }, - [props.editingActive, activePolyline, appendLocation] + [ + editingActive, + activePolylineId, + appendLocation, + polylines, + referencePathPointIndex, + draggedPointIndex, + onEditingDone, + ] ); const disablePanning = React.useCallback( @@ -301,16 +378,15 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita if (info.editableEntity?.type === "point") { setDraggedPointIndex(info.editableEntity.index); disablePanning(); + return true; } - - return true; }, [disablePanning] ); const onDrag = React.useCallback( function onDrag(info: PickingInfo) { - if (draggedPointIndex === null || !activePolyline || !info.coordinate) { + if (draggedPointIndex === null || !activePolylineId || !info.coordinate) { return false; } @@ -326,7 +402,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita x: info.x, y: info.y, radius: 10, - depth: 2, + depth: 1, unproject3D: true, }); @@ -340,21 +416,29 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita return false; } - const newPath = [...activePolyline.path]; - newPath[draggedPointIndex] = [...firstLayerInfos.coordinate]; + setPolylines((prevPolylines) => { + const activePolyline = prevPolylines.find((polyline) => polyline.id === activePolylineId); + if (!activePolyline || !firstLayerInfos.coordinate) { + return prevPolylines; + } + + const newPath = [...activePolyline.path]; + newPath[draggedPointIndex] = [...firstLayerInfos.coordinate]; - setActivePolyline({ - ...activePolyline, - path: newPath, + const newPolyline: Polyline = { + ...activePolyline, + path: newPath, + }; + return prevPolylines.map((polyline) => (polyline.id === activePolyline.id ? newPolyline : polyline)); }); return false; }, - [draggedPointIndex, activePolyline] + [draggedPointIndex, activePolylineId, props.deckGlRef] ); const onDragEnd = React.useCallback( - function onDragEnd(info: PickingInfo) { + function onDragEnd() { setDraggedPointIndex(null); enablePanning(); }, @@ -364,16 +448,19 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita const layers: Layer[] = [ new PolylinesLayer({ id: "polylines-layer", - polylines, + polylines: polylines.filter((polyline) => polyline.id !== activePolylineId), + selectedPolylineId: selectedPolylineId ?? undefined, }), ]; - if (activePolyline) { + if (activePolylineId) { + const activePolyline = polylines.find((polyline) => polyline.id === activePolylineId); layers.push( new EditablePolylineLayer({ id: "editable-polylines-layer", polyline: activePolyline, mouseHoverPoint: hoverPoint ?? undefined, + referencePathPointIndex, onDragStart, onDragEnd, }) @@ -386,6 +473,10 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita return "grabbing"; } + if (cursorIcon === CursorIcon.POINTER) { + return "pointer"; + } + if (cursorIcon === CursorIcon.CONTINUE_FROM_POINT) { return `url(${continuePathIcon}), crosshair`; } @@ -398,13 +489,13 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita return `url(${removePathIcon}), crosshair`; } - if (activePolyline) { + if (activePolylineId && referencePathPointIndex !== undefined) { return `url(${setPathPointIcon}), crosshair`; } return "default"; }, - [cursorIcon, activePolyline] + [cursorIcon, activePolylineId, referencePathPointIndex] ); return { @@ -413,6 +504,8 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita onDrag, getCursor, disableCameraInteraction: draggedPointIndex !== null, + contextMenuItems, + cursorPosition, }; } diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts index 8309ac056..1499a606f 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts @@ -1,5 +1,14 @@ +import React from "react"; + export type Polyline = { id: string; + name: string; color: [number, number, number, number]; - polyline: number[][]; + path: number[][]; +}; + +export type ContextMenuItem = { + icon?: React.ReactNode; + label: string; + onClick: () => void; }; diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerISetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerISetting.tsx new file mode 100644 index 000000000..c20464198 --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerISetting.tsx @@ -0,0 +1,101 @@ +import React from "react"; + +import { Dropdown, DropdownOption } from "@lib/components/Dropdown"; + +import { SettingDelegate } from "../../delegates/SettingDelegate"; +import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; +import { SettingRegistry } from "../SettingRegistry"; +import { SettingType } from "../settingsTypes"; + +type ValueType = number | null; + +export class GridLayerISetting implements Setting { + private _delegate: SettingDelegate; + + constructor() { + this._delegate = new SettingDelegate(null, this); + } + + getType(): SettingType { + return SettingType.GRID_LAYER_I; + } + + getLabel(): string { + return "Grid layer I"; + } + + getDelegate(): SettingDelegate { + return this._delegate; + } + + isValueValid(availableValues: AvailableValuesType, value: ValueType): boolean { + if (value === null) { + return false; + } + + if (availableValues.length < 3) { + return false; + } + + const min = 0; + const max = availableValues[0]; + + if (max === null) { + return false; + } + + return value >= min && value <= max; + } + + fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { + if (availableValues.length < 3) { + return null; + } + + const min = 0; + const max = availableValues[0]; + + if (max === null) { + return null; + } + + if (currentValue === null) { + return min; + } + + if (currentValue < min) { + return min; + } + + if (currentValue > max) { + return max; + } + + return currentValue; + } + + makeComponent(): (props: SettingComponentProps) => React.ReactNode { + return function Ensemble(props: SettingComponentProps) { + const iRange = props.availableValues ? Array.from({ length: props.availableValues[0] }, (_, i) => i) : []; + + const options: DropdownOption[] = iRange.map((value) => { + return { + value: value.toString(), + label: value === null ? "None" : value.toString(), + }; + }); + + return ( + props.onValueChange(parseInt(val))} + disabled={props.isOverridden} + showArrows + /> + ); + }; + } +} + +SettingRegistry.registerSetting(GridLayerISetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJSetting.tsx new file mode 100644 index 000000000..3e0a2bd57 --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJSetting.tsx @@ -0,0 +1,101 @@ +import React from "react"; + +import { Dropdown, DropdownOption } from "@lib/components/Dropdown"; + +import { SettingDelegate } from "../../delegates/SettingDelegate"; +import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; +import { SettingRegistry } from "../SettingRegistry"; +import { SettingType } from "../settingsTypes"; + +type ValueType = number | null; + +export class GridLayerJSetting implements Setting { + private _delegate: SettingDelegate; + + constructor() { + this._delegate = new SettingDelegate(null, this); + } + + getType(): SettingType { + return SettingType.GRID_LAYER_J; + } + + getLabel(): string { + return "Grid layer J"; + } + + getDelegate(): SettingDelegate { + return this._delegate; + } + + isValueValid(availableValues: AvailableValuesType, value: ValueType): boolean { + if (value === null) { + return false; + } + + if (availableValues.length < 3) { + return false; + } + + const min = 0; + const max = availableValues[1]; + + if (max === null) { + return false; + } + + return value >= min && value <= max; + } + + fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { + if (availableValues.length < 3) { + return null; + } + + const min = 0; + const max = availableValues[1]; + + if (max === null) { + return null; + } + + if (currentValue === null) { + return min; + } + + if (currentValue < min) { + return min; + } + + if (currentValue > max) { + return max; + } + + return currentValue; + } + + makeComponent(): (props: SettingComponentProps) => React.ReactNode { + return function Ensemble(props: SettingComponentProps) { + const iRange = props.availableValues ? Array.from({ length: props.availableValues[1] }, (_, i) => i) : []; + + const options: DropdownOption[] = iRange.map((value) => { + return { + value: value.toString(), + label: value === null ? "None" : value.toString(), + }; + }); + + return ( + props.onValueChange(parseInt(val))} + disabled={props.isOverridden} + showArrows + /> + ); + }; + } +} + +SettingRegistry.registerSetting(GridLayerJSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerKSetting.tsx similarity index 93% rename from frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerSetting.tsx rename to frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerKSetting.tsx index b4fd2c994..72c30bc6b 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerKSetting.tsx @@ -9,7 +9,7 @@ import { SettingType } from "../settingsTypes"; type ValueType = number | null; -export class GridLayerSetting implements Setting { +export class GridLayerKSetting implements Setting { private _delegate: SettingDelegate; constructor() { @@ -17,11 +17,11 @@ export class GridLayerSetting implements Setting { } getType(): SettingType { - return SettingType.GRID_LAYER; + return SettingType.GRID_LAYER_K; } getLabel(): string { - return "Grid layer"; + return "Grid layer K"; } getDelegate(): SettingDelegate { @@ -98,4 +98,4 @@ export class GridLayerSetting implements Setting { } } -SettingRegistry.registerSetting(GridLayerSetting); +SettingRegistry.registerSetting(GridLayerKSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts index f7430c181..5fe34db47 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts +++ b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts @@ -11,6 +11,8 @@ export enum SettingType { SMDA_WELLBORE_HEADERS = "smdaWellboreHeaders", GRID_NAME = "gridName", GRID_ATTRIBUTE = "gridAttribute", - GRID_LAYER = "gridLayer", + GRID_LAYER_I = "gridLayerI", + GRID_LAYER_J = "gridLayerJ", + GRID_LAYER_K = "gridLayerK", SHOW_GRID_LINES = "showGridLines", } From 2c97b71d593d4903b9072be574d3bab81d98bf08 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 22 Jan 2025 12:49:22 +0100 Subject: [PATCH 12/97] wip --- .../3DViewerNew/view/components/Toolbar.tsx | 15 +- .../deckGlLayers/EditablePolylineLayer.ts | 172 ++++++++++++------ .../deckGlLayers/PolylinesLayer.ts | 70 ++++--- 3 files changed, 160 insertions(+), 97 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx index 4e7215ffe..c501c0ccb 100644 --- a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx @@ -1,21 +1,10 @@ import React from "react"; -import PathIconSvg from "@assets/path.svg"; -import { Icon } from "@equinor/eds-core-react"; -import { IconData } from "@equinor/eds-icons"; import { Button } from "@lib/components/Button"; import { HoldPressedIntervalCallbackButton } from "@lib/components/HoldPressedIntervalCallbackButton/holdPressedIntervalCallbackButton"; import { ToggleButton } from "@lib/components/ToggleButton"; import { Toolbar as GenericToolbar, ToolBarDivider } from "@modules/_shared/components/Toolbar"; -import { Add, FilterCenterFocus, GridOff, GridOn, Remove } from "@mui/icons-material"; - -const PathIcon: IconData = { - name: "path", - prefix: "path", - height: "24", - width: "24", - svgPathData: PathIconSvg, -}; +import { Add, FilterCenterFocus, GridOff, GridOn, Polyline, Remove } from "@mui/icons-material"; export type ToolbarProps = { verticalScale: number; @@ -60,7 +49,7 @@ export function Toolbar(props: ToolbarProps): React.ReactNode { title="Edit polylines" active={props.polylineEditingActive} > - + [] = []; + if (referencePathPointIndex !== undefined && mouseHoverPoint && this.state.hoveredEntity === null) { + layers.push( + new LineLayer({ + id: "line", + data: [{ from: polyline.path[referencePathPointIndex], to: mouseHoverPoint }], + getSourcePosition: (d) => d.from, + getTargetPosition: (d) => d.to, + getColor: [230, 136, 21, 100], + getWidth: 10, + widthUnits: "meters", + widthMinPixels: 3, + parameters: { + depthTest: false, + }, + }), + new ColumnLayer({ + id: "hover-point", + data: [mouseHoverPoint], + getElevation: 1, + getPosition: (d) => d, + getFillColor: [230, 136, 21, 255], + getLineColor: [230, 136, 21, 255], + getLineWidth: 10, + extruded: false, + radius: 30, + lineWidthMinPixels: 5, + lineWidthMaxPixels: 5, + radiusUnits: "pixels", + lineWidthUnits: "pixels", + pickable: false, + parameters: { + depthTest: false, + }, + }) + ); + } + const polylinePathLayerData: number[][][] = []; for (let i = 0; i < polyline.path.length - 1; i++) { polylinePathLayerData.push([polyline.path[i], polyline.path[i + 1]]); } + if (this.state.hoveredEntity && this.state.hoveredEntity.layer === "line") { + const hoveredLine = polylinePathLayerData[this.state.hoveredEntity.index]; + layers.push( + new PathLayer({ + id: "hovered-line", + data: [hoveredLine], + getPath: (d) => d, + getColor: [255, 255, 255, 50], + getWidth: 20, + widthUnits: "meters", + widthMinPixels: 6, + parameters: { + depthTest: false, + }, + pickable: false, + }) + ); + } + layers.push( new AnimatedPathLayer({ id: "lines", @@ -108,6 +164,8 @@ export class EditablePolylineLayer extends CompositeLayer d, getWidth: 50, + widthMinPixels: 6, + widthMaxPixels: 10, billboard: true, widthUnits: "meters", parameters: { depthTest: false, }, pickable: true, - }), - new ColumnLayer({ + }) + ); + layers.push( + new ScatterplotLayer({ + id: "points-selection", + data: polyline.path, + getPosition: (d) => d, + getRadius: (d, context) => { + if ( + this.state.hoveredEntity?.layer === "point" && + context.index === this.state.hoveredEntity.index + ) { + return 10; + } + return 5; + }, + getFillColor: [255, 255, 255, 1], + getLineColor: [0, 0, 0, 0], + getLineWidth: 30, + lineWidthMinPixels: 10, + radiusUnits: "pixels", + pickable: true, + parameters: { + depthTest: false, + }, + updateTriggers: { + getRadius: [this.state.hoveredEntity, referencePathPointIndex], + }, + }) + ); + + layers.push( + new ScatterplotLayer({ id: "points", data: polyline.path, - getElevation: 1, getPosition: (d) => d, getFillColor: (d, context) => { if (context.index === referencePathPointIndex) { - return [230, 136, 21, 255]; + return [255, 255, 255, 255]; } - return [255, 255, 255, 255]; + return [230, 136, 21, 255]; + }, + getLineColor: (d, context) => { + if (context.index === referencePathPointIndex) { + return [255, 255, 255, 255]; + } + return [230, 136, 21, 255]; }, - getLineColor: [230, 136, 21, 255], getLineWidth: (d, context) => { if ( this.state.hoveredEntity && @@ -151,67 +246,28 @@ export class EditablePolylineLayer extends CompositeLayer { + if ( + this.state.hoveredEntity?.layer === "point" && + context.index === this.state.hoveredEntity.index + ) { + return 10; + } + return 5; + }, radiusUnits: "pixels", pickable: false, parameters: { depthTest: false, }, updateTriggers: { - getFillColor: [this.state.hoveredEntity, referencePathPointIndex], - getLineWidth: [this.state.hoveredEntity, referencePathPointIndex], - }, - }), - new ColumnLayer({ - id: "points-selection", - data: polyline.path, - getElevation: 1, - getPosition: (d) => d, - getFillColor: [255, 255, 255, 1], - getLineColor: [0, 0, 0, 0], - getLineWidth: 1, - extruded: false, - radius: 40, - radiusUnits: "pixels", - pickable: true, - parameters: { - depthTest: false, + getLineColor: [referencePathPointIndex], + getFillColor: [referencePathPointIndex], + getRadius: [this.state.hoveredEntity, referencePathPointIndex], }, }) ); - if (referencePathPointIndex !== undefined && mouseHoverPoint && this.state.hoveredEntity === null) { - layers.push( - new LineLayer({ - id: "line", - data: [{ from: polyline.path[referencePathPointIndex], to: mouseHoverPoint }], - getSourcePosition: (d) => d.from, - getTargetPosition: (d) => d.to, - getColor: [230, 136, 21, 100], - getWidth: 2, - parameters: { - depthTest: false, - }, - }), - new ColumnLayer({ - id: "hover-point", - data: [mouseHoverPoint], - getElevation: 1, - getPosition: (d) => d, - getFillColor: [230, 136, 21, 255], - extruded: false, - radius: 20, - radiusUnits: "pixels", - pickable: false, - parameters: { - depthTest: false, - }, - }) - ); - } - return layers; } } diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts index c6ea053b9..a95dae0e7 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts @@ -1,4 +1,4 @@ -import { CompositeLayer, GetPickingInfoParams, Layer, PickingInfo } from "@deck.gl/core"; +import { CompositeLayer, FilterContext, GetPickingInfoParams, Layer, PickingInfo } from "@deck.gl/core"; import { PathLayer, TextLayer } from "@deck.gl/layers"; import { Polyline } from "../types"; @@ -47,6 +47,14 @@ export class PolylinesLayer extends CompositeLayer { return info; } + filterSubLayer(context: FilterContext): boolean { + if (context.layer.id.includes("labels")) { + return context.viewport.zoom > -4; + } + + return true; + } + renderLayers(): Layer[] { const { hoveredPolylineIndex } = this.state; @@ -63,6 +71,7 @@ export class PolylinesLayer extends CompositeLayer { getColor: (d: Polyline) => [d.color[0], d.color[1], d.color[2], 200], getWidth: 30, widthUnits: "meters", + widthMinPixels: 5, parameters: { depthTest: false, }, @@ -80,6 +89,7 @@ export class PolylinesLayer extends CompositeLayer { getColor: (d: Polyline) => [d.color[0], d.color[1], d.color[2], 100], getWidth: 30, widthUnits: "meters", + widthMinPixels: 6, parameters: { depthTest: false, }, @@ -89,30 +99,27 @@ export class PolylinesLayer extends CompositeLayer { const polylineLabels: { label: string; position: number[]; angle: number; color: number[] }[] = []; for (const polyline of this.props.polylines) { - for (let i = 0; i < polyline.path.length - 1; i++) { - const vector = [ - polyline.path[i + 1][0] - polyline.path[i][0], - polyline.path[i + 1][1] - polyline.path[i][1], - polyline.path[i + 1][2] - polyline.path[i][2], - ]; - const length = Math.sqrt(vector[0] ** 2 + vector[1] ** 2); - const unitVector = [vector[0] / length, vector[1] / length, vector[2] / length]; - const normalVector = [-unitVector[1], unitVector[0], 0]; - let angle = Math.atan2(unitVector[1], unitVector[0]) * (180 / Math.PI); - if (angle > 90 || angle < -90) { - angle += 180; - } - polylineLabels.push({ - label: polyline.name, - position: [ - polyline.path[i][0] + (unitVector[0] * length) / 2 + (normalVector[0] * 100) / 2, - polyline.path[i][1] + (unitVector[1] * length) / 2 + (normalVector[1] * 100) / 2, - polyline.path[i][2] + (unitVector[2] * length) / 2 + (normalVector[2] * 100) / 2, - ], - angle, - color: polyline.color, - }); + const vector = [ + polyline.path[1][0] - polyline.path[0][0], + polyline.path[1][1] - polyline.path[0][1], + polyline.path[1][2] - polyline.path[0][2], + ]; + const length = Math.sqrt(vector[0] ** 2 + vector[1] ** 2); + const unitVector = [vector[0] / length, vector[1] / length, vector[2] / length]; + let angle = Math.atan2(unitVector[1], unitVector[0]) * (180 / Math.PI); + if (angle > 90 || angle < -90) { + angle += 180; } + polylineLabels.push({ + label: polyline.name, + position: [ + polyline.path[0][0] + (unitVector[0] * length) / 2, + polyline.path[0][1] + (unitVector[1] * length) / 2, + polyline.path[0][2] + (unitVector[2] * length) / 2, + ], + angle, + color: polyline.color, + }); } layers.push( @@ -122,7 +129,10 @@ export class PolylinesLayer extends CompositeLayer { getPath: (d) => d.path, getColor: (d: Polyline) => d.color, getWidth: 10, + billboard: false, widthUnits: "meters", + widthMinPixels: 3, + widthMaxPixels: 10, pickable: false, parameters: { depthTest: false, @@ -135,13 +145,19 @@ export class PolylinesLayer extends CompositeLayer { getText: (d) => d.label, getSize: 12, sizeUnits: "meters", - sizeMinPixels: 12, + sizeMinPixels: 16, getAngle: (d) => d.angle, - getColor: (d: Polyline) => d.color, + getColor: [0, 0, 0], parameters: { depthTest: false, }, billboard: false, + getBackgroundColor: [255, 255, 255, 100], + getBackgroundPadding: [10, 10], + getBackgroundBorderColor: [0, 0, 0, 255], + getBackgroundBorderWidth: 2, + getBackgroundElevation: 1, + background: true, }), new PathLayer({ id: `polylines-hoverable`, @@ -150,6 +166,8 @@ export class PolylinesLayer extends CompositeLayer { getColor: (d: Polyline) => [d.color[0], d.color[1], d.color[2], 1], getWidth: 50, widthUnits: "meters", + widthMinPixels: 10, + widthMaxPixels: 20, pickable: true, parameters: { depthTest: false, From 51f022ade9049289044fbeb811eca3eb6304e49f Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 22 Jan 2025 16:42:21 +0100 Subject: [PATCH 13/97] wip --- frontend/src/assets/add_path_point.svg | 22 +++ frontend/src/assets/remove_path_point.svg | 21 +++ .../components/ToggleButton/toggleButton.tsx | 6 + .../src/lib/icons/icons/addPathPointIcon.tsx | 11 ++ frontend/src/lib/icons/icons/pathIcon.tsx | 10 ++ .../lib/icons/icons/removePathPointIcon.tsx | 11 ++ frontend/src/lib/icons/index.ts | 3 + .../view/components/ReadoutWrapper.tsx | 66 ++++++-- .../3DViewerNew/view/components/Toolbar.tsx | 151 ++++++++++++++---- .../deckGlLayers/EditablePolylineLayer.ts | 29 ++-- .../deckGlLayers/PolylinesLayer.ts | 4 + .../editablePolylinesHook.tsx | 121 +++++++++----- .../view/hooks/editablePolylines/types.ts | 8 + 13 files changed, 364 insertions(+), 99 deletions(-) create mode 100644 frontend/src/assets/add_path_point.svg create mode 100644 frontend/src/assets/remove_path_point.svg create mode 100644 frontend/src/lib/icons/icons/addPathPointIcon.tsx create mode 100644 frontend/src/lib/icons/icons/pathIcon.tsx create mode 100644 frontend/src/lib/icons/icons/removePathPointIcon.tsx create mode 100644 frontend/src/lib/icons/index.ts diff --git a/frontend/src/assets/add_path_point.svg b/frontend/src/assets/add_path_point.svg new file mode 100644 index 000000000..d7b020df4 --- /dev/null +++ b/frontend/src/assets/add_path_point.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/remove_path_point.svg b/frontend/src/assets/remove_path_point.svg new file mode 100644 index 000000000..0ec59f316 --- /dev/null +++ b/frontend/src/assets/remove_path_point.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/lib/components/ToggleButton/toggleButton.tsx b/frontend/src/lib/components/ToggleButton/toggleButton.tsx index a4a1003ea..9add081d8 100644 --- a/frontend/src/lib/components/ToggleButton/toggleButton.tsx +++ b/frontend/src/lib/components/ToggleButton/toggleButton.tsx @@ -12,6 +12,12 @@ export type ToggleButtonProps = ButtonUnstyledProps & { export const ToggleButton = React.forwardRef((props: ToggleButtonProps, ref: React.ForwardedRef) => { const { active, onToggle, ...other } = props; const [isActive, setIsActive] = React.useState(active); + const [prevActive, setPrevActive] = React.useState(active); + + if (active !== prevActive) { + setIsActive(active); + setPrevActive(active); + } const handleClick = React.useCallback(() => { setIsActive(!isActive); diff --git a/frontend/src/lib/icons/icons/addPathPointIcon.tsx b/frontend/src/lib/icons/icons/addPathPointIcon.tsx new file mode 100644 index 000000000..77d5da3be --- /dev/null +++ b/frontend/src/lib/icons/icons/addPathPointIcon.tsx @@ -0,0 +1,11 @@ +import { createSvgIcon } from "@mui/material"; + +export const AddPathPointIcon = createSvgIcon( + + + + + + , + "AddPathPoint" +); diff --git a/frontend/src/lib/icons/icons/pathIcon.tsx b/frontend/src/lib/icons/icons/pathIcon.tsx new file mode 100644 index 000000000..39b6d38b0 --- /dev/null +++ b/frontend/src/lib/icons/icons/pathIcon.tsx @@ -0,0 +1,10 @@ +import { createSvgIcon } from "@mui/material"; + +export const DrawPathIcon = createSvgIcon( + + + + + , + "DrawPath" +); diff --git a/frontend/src/lib/icons/icons/removePathPointIcon.tsx b/frontend/src/lib/icons/icons/removePathPointIcon.tsx new file mode 100644 index 000000000..c643ac9e6 --- /dev/null +++ b/frontend/src/lib/icons/icons/removePathPointIcon.tsx @@ -0,0 +1,11 @@ +import { createSvgIcon } from "@mui/material"; + +export const RemovePathPointIcon = createSvgIcon( + + + + + + , + "RemovePathPoint" +); diff --git a/frontend/src/lib/icons/index.ts b/frontend/src/lib/icons/index.ts new file mode 100644 index 000000000..efa0a00f2 --- /dev/null +++ b/frontend/src/lib/icons/index.ts @@ -0,0 +1,3 @@ +export { DrawPathIcon } from "./icons/pathIcon"; +export { AddPathPointIcon } from "./icons/addPathPointIcon"; +export { RemovePathPointIcon } from "./icons/removePathPointIcon"; diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index d5ceafefc..acfb6e369 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -8,10 +8,13 @@ import { SubsurfaceViewerWithCameraState } from "@modules/_shared/components/Sub import { BoundingBox3D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; +import { isEqual } from "lodash"; + import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; import { Toolbar } from "./Toolbar"; import { useEditablePolylines } from "../hooks/editablePolylines/editablePolylinesHook"; +import { Polyline, PolylineEditingMode } from "../hooks/editablePolylines/types"; export type ReadooutWrapperProps = { views: ViewsType; @@ -29,19 +32,29 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const [layerPickingInfo, setLayerPickingInfo] = React.useState([]); const [gridVisible, setGridVisible] = React.useState(false); const [verticalScale, setVerticalScale] = React.useState(1); - const [polylineEditingActive, setPolylineEditingActive] = React.useState(false); - - const onEditingDone = React.useCallback(function onEditingDone() { - setPolylineEditingActive(false); - }, []); - - const { onMouseEvent, layers, onDrag, getCursor, cursorPosition, contextMenuItems } = useEditablePolylines({ + const [polylineEditingMode, setPolylineEditingMode] = React.useState(PolylineEditingMode.NONE); + const [polylines, setPolylines] = React.useState([]); + + const { + onMouseEvent, + layers, + onDrag, + getCursor, + cursorPosition, + contextMenuItems, + activePolylineId, + polylines: changedPolylines, + } = useEditablePolylines({ deckGlRef, - polylines: [], - editingActive: polylineEditingActive, - onEditingDone, + polylines, + editingMode: polylineEditingMode, + onEditingModeChange: handlePolylineEditingModeChange, }); + if (!isEqual(changedPolylines, polylines)) { + setPolylines(changedPolylines); + } + function handleFitInViewClick() { setTriggerHomeCounter((prev) => prev + 1); } @@ -50,8 +63,8 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { setGridVisible(visible); } - function handleEditPolylines() { - setPolylineEditingActive((prev) => !prev); + function handlePolylineEditingModeChange(mode: PolylineEditingMode) { + setPolylineEditingMode(mode); } function handleMouseHover(event: MapMouseEvent): void { @@ -74,6 +87,28 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { onDrag(info); } + const handlePolylineNameChange = React.useCallback( + function handlePolylineNameChange(name: string): void { + if (!activePolylineId) { + return; + } + + setPolylines((prev) => + prev.map((polyline) => { + if (polyline.id === activePolylineId) { + return { + ...polyline, + name, + }; + } + + return polyline; + }) + ); + }, + [activePolylineId] + ); + let adjustedLayers = [...props.layers]; if (!gridVisible) { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); @@ -86,10 +121,13 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { p.id === activePolylineId)?.name} /> {cursorPosition && contextMenuItems.length && ( diff --git a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx index c501c0ccb..a540fb000 100644 --- a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx @@ -2,21 +2,37 @@ import React from "react"; import { Button } from "@lib/components/Button"; import { HoldPressedIntervalCallbackButton } from "@lib/components/HoldPressedIntervalCallbackButton/holdPressedIntervalCallbackButton"; +import { Input } from "@lib/components/Input"; import { ToggleButton } from "@lib/components/ToggleButton"; +import { AddPathPointIcon, DrawPathIcon, RemovePathPointIcon } from "@lib/icons/"; import { Toolbar as GenericToolbar, ToolBarDivider } from "@modules/_shared/components/Toolbar"; import { Add, FilterCenterFocus, GridOff, GridOn, Polyline, Remove } from "@mui/icons-material"; +import { PolylineEditingMode } from "../hooks/editablePolylines/types"; + export type ToolbarProps = { verticalScale: number; - polylineEditingActive: boolean; + polylineEditingMode: PolylineEditingMode; + hasActivePolyline: boolean; + activePolylineName?: string; onFitInView: () => void; onGridVisibilityChange: (visible: boolean) => void; - onToggleEditPolyline: () => void; + onPolylineEditingModeChange: (mode: PolylineEditingMode) => void; onVerticalScaleChange(value: number): void; + onPolylineNameChange: (name: string) => void; }; export function Toolbar(props: ToolbarProps): React.ReactNode { - const [gridVisible, setGridVisible] = React.useState(false); + const [gridVisible, setGridVisible] = React.useState(false); + const [polylineEditingMode, setPolylineEditingMode] = React.useState(PolylineEditingMode.NONE); + const [prevPolylineEditingMode, setPrevPolylineEditingMode] = React.useState( + PolylineEditingMode.NONE + ); + + if (props.polylineEditingMode !== prevPolylineEditingMode) { + setPolylineEditingMode(props.polylineEditingMode); + setPrevPolylineEditingMode(props.polylineEditingMode); + } function handleFitInViewClick() { props.onFitInView(); @@ -35,38 +51,107 @@ export function Toolbar(props: ToolbarProps): React.ReactNode { props.onVerticalScaleChange(props.verticalScale - 0.1); } + function handleTogglePolylineEditing() { + if (polylineEditingMode !== PolylineEditingMode.NONE) { + setPolylineEditingMode(PolylineEditingMode.NONE); + props.onPolylineEditingModeChange(PolylineEditingMode.NONE); + return; + } + setPolylineEditingMode(PolylineEditingMode.IDLE); + props.onPolylineEditingModeChange(PolylineEditingMode.IDLE); + } + + function handlePolylineEditingModeChange(mode: PolylineEditingMode) { + setPolylineEditingMode(mode); + props.onPolylineEditingModeChange(mode); + } + + function handlePolylineNameChange(event: React.ChangeEvent) { + props.onPolylineNameChange(event.target.value); + } + return ( - - - {gridVisible ? : } - - - - - - - - - - - {props.verticalScale.toFixed(2)} - - - - +
+
+ + + {gridVisible ? : } + + + + + + + + + + + {props.verticalScale.toFixed(2)} + + + + +
+ {polylineEditingMode !== PolylineEditingMode.NONE && ( + <> +
+ + handlePolylineEditingModeChange( + active ? PolylineEditingMode.DRAW : PolylineEditingMode.IDLE + ) + } + > + + + + handlePolylineEditingModeChange( + active ? PolylineEditingMode.ADD_POINT : PolylineEditingMode.IDLE + ) + } + > + + + + handlePolylineEditingModeChange( + active ? PolylineEditingMode.REMOVE_POINT : PolylineEditingMode.IDLE + ) + } + > + + + +
+ + )} +
); } diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts index 068a58d60..21fb73058 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts @@ -52,7 +52,7 @@ export class EditablePolylineLayer extends CompositeLayer { - if (context.index === referencePathPointIndex) { + if ( + this.state.hoveredEntity && + this.state.hoveredEntity.layer === "point" && + context.index === this.state.hoveredEntity.index + ) { return [255, 255, 255, 255]; } - return [230, 136, 21, 255]; + return [0, 0, 0, 0]; }, getLineWidth: (d, context) => { if ( @@ -242,26 +248,31 @@ export class EditablePolylineLayer extends CompositeLayer { if ( this.state.hoveredEntity?.layer === "point" && context.index === this.state.hoveredEntity.index ) { - return 10; + return 12; } - return 5; + return 10; }, + stroked: true, radiusUnits: "pixels", - pickable: false, + lineWidthUnits: "meters", + lineWidthMinPixels: 3, + radiusMinPixels: 5, + pickable: true, parameters: { depthTest: false, }, updateTriggers: { - getLineColor: [referencePathPointIndex], + getLineWidth: [this.state.hoveredEntity, referencePathPointIndex], + getLineColor: [this.state.hoveredEntity], getFillColor: [referencePathPointIndex], getRadius: [this.state.hoveredEntity, referencePathPointIndex], }, diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts index a95dae0e7..dd1d01089 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts @@ -7,6 +7,7 @@ export type PolylinesLayerProps = { id: string; polylines: Polyline[]; selectedPolylineId?: string; + hoverable?: boolean; }; export type PolylinesLayerPickingInfo = PickingInfo & { @@ -26,6 +27,9 @@ export class PolylinesLayer extends CompositeLayer { }; onHover(info: PickingInfo): boolean { + if (!this.props.hoverable) { + return false; + } if (info.index === undefined) { return false; } diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx index 4a2ea1972..ca247ccae 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx @@ -14,13 +14,13 @@ import { v4 } from "uuid"; import { EditablePolylineLayer, isEditablePolylineLayerPickingInfo } from "./deckGlLayers/EditablePolylineLayer"; import { PolylinesLayer, isPolylinesLayerPickingInfo } from "./deckGlLayers/PolylinesLayer"; -import { ContextMenuItem, Polyline } from "./types"; +import { ContextMenuItem, Polyline, PolylineEditingMode } from "./types"; export type UseEditablePolylinesProps = { deckGlRef: React.MutableRefObject; - editingActive: boolean; + editingMode: PolylineEditingMode; polylines: Polyline[]; - onEditingDone?: (polylines: Polyline[]) => void; + onEditingModeChange?: (mode: PolylineEditingMode) => void; }; export type UseEditablePolylinesReturnType = { @@ -31,6 +31,8 @@ export type UseEditablePolylinesReturnType = { getCursor: DeckGLProps["getCursor"]; cursorPosition: number[] | null; contextMenuItems: ContextMenuItem[]; + activePolylineId: string | null; + polylines: Polyline[]; }; enum CursorIcon { @@ -47,13 +49,12 @@ enum PathAppendLocation { } export function useEditablePolylines(props: UseEditablePolylinesProps): UseEditablePolylinesReturnType { - const { onEditingDone } = props; + const { onEditingModeChange } = props; const [hoverPoint, setHoverPoint] = React.useState(null); const [activePolylineId, setActivePolylineId] = React.useState(null); const [polylines, setPolylines] = React.useState(props.polylines); const [prevPolylines, setPrevPolylines] = React.useState(props.polylines); - const [prevEditingActive, setPrevEditingActive] = React.useState(props.editingActive); const [cursorIcon, setCursorIcon] = React.useState(null); const [cursorPosition, setCursorPosition] = React.useState(null); const [appendLocation, setAppendLocation] = React.useState(PathAppendLocation.END); @@ -61,32 +62,44 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita const [contextMenuItems, setContextMenuItems] = React.useState([]); const [selectedPolylineId, setSelectedPolylineId] = React.useState(null); const [referencePathPointIndex, setReferencePathPointIndex] = React.useState(undefined); - const [editingActive, setEditingActive] = React.useState(props.editingActive); + const [prevEditingMode, setPrevEditingMode] = React.useState(PolylineEditingMode.NONE); + + let changedPolylines = polylines; if (!isEqual(props.polylines, prevPolylines)) { setPolylines(props.polylines); setPrevPolylines(props.polylines); + changedPolylines = props.polylines; } - if (!isEqual(props.editingActive, prevEditingActive)) { - setPrevEditingActive(props.editingActive); - setActivePolylineId(null); - setEditingActive(props.editingActive); + if (props.editingMode !== prevEditingMode) { + if (props.editingMode === PolylineEditingMode.NONE) { + setActivePolylineId(null); + setReferencePathPointIndex(undefined); + } + if (props.editingMode === PolylineEditingMode.IDLE) { + setReferencePathPointIndex(undefined); + } + setPrevEditingMode(props.editingMode); } - React.useEffect(function onMount() { - function onKeyUp(event: KeyboardEvent) { - if (event.key === "Escape") { - setReferencePathPointIndex(undefined); + React.useEffect( + function onMount() { + function onKeyUp(event: KeyboardEvent) { + if (event.key === "Escape") { + setReferencePathPointIndex(undefined); + onEditingModeChange?.(PolylineEditingMode.IDLE); + } } - } - window.addEventListener("keyup", onKeyUp); + window.addEventListener("keyup", onKeyUp); - return () => { - window.removeEventListener("keyup", onKeyUp); - }; - }, []); + return () => { + window.removeEventListener("keyup", onKeyUp); + }; + }, + [onEditingModeChange] + ); const onMouseEvent = React.useCallback( (event: MapMouseEvent) => { @@ -119,6 +132,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita firstLayerInfos.coordinate ) { if ( + [PolylineEditingMode.DRAW, PolylineEditingMode.ADD_POINT].includes(props.editingMode) && editablePolylineLayerPickingInfo.editableEntity && editablePolylineLayerPickingInfo.editableEntity.type === "line" ) { @@ -126,6 +140,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita return; } if ( + [PolylineEditingMode.DRAW, PolylineEditingMode.REMOVE_POINT].includes(props.editingMode) && activePolyline && editablePolylineLayerPickingInfo.editableEntity && editablePolylineLayerPickingInfo.editableEntity.type === "point" @@ -146,7 +161,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita const polylinesLayerPickingInfo = event.infos.find(isPolylinesLayerPickingInfo); - if (polylinesLayerPickingInfo) { + if (polylinesLayerPickingInfo && props.editingMode === PolylineEditingMode.IDLE) { setCursorIcon(CursorIcon.POINTER); return; } @@ -166,7 +181,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita return; } - if (!editingActive) { + if (props.editingMode === PolylineEditingMode.IDLE && !activePolylineId) { const polylinesLayerPickingInfo = event.infos.find(isPolylinesLayerPickingInfo); if ( !polylinesLayerPickingInfo || @@ -191,7 +206,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita onClick: () => { setActivePolylineId(polylinesLayerPickingInfo.polylineId ?? null); setReferencePathPointIndex(undefined); - setEditingActive(true); + onEditingModeChange?.(PolylineEditingMode.DRAW); setSelectedPolylineId(null); setContextMenuItems([]); }, @@ -223,7 +238,10 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita editablePolylineLayerPickingInfo.editableEntity ) { // Remove point - if (editablePolylineLayerPickingInfo.editableEntity.type === "point") { + if ( + editablePolylineLayerPickingInfo.editableEntity.type === "point" && + [PolylineEditingMode.DRAW, PolylineEditingMode.REMOVE_POINT].includes(props.editingMode) + ) { const index = editablePolylineLayerPickingInfo.editableEntity.index; if ( @@ -267,7 +285,10 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita return; } - if (editablePolylineLayerPickingInfo.editableEntity.type === "line") { + if ( + editablePolylineLayerPickingInfo.editableEntity.type === "line" && + [PolylineEditingMode.DRAW, PolylineEditingMode.ADD_POINT].includes(props.editingMode) + ) { const newPath = [...activePolyline.path]; newPath.splice(editablePolylineLayerPickingInfo.editableEntity.index + 1, 0, [ ...firstLayerInfos.coordinate, @@ -298,7 +319,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita } } - if (!activePolyline) { + if (!activePolyline && props.editingMode === PolylineEditingMode.DRAW) { const id = v4(); const newPolyline: Polyline = { id, @@ -312,32 +333,39 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita } else if (activePolyline) { if (referencePathPointIndex === undefined) { setActivePolylineId(null); - setEditingActive(false); - onEditingDone?.(polylines); + onEditingModeChange?.(PolylineEditingMode.IDLE); return; } - const newPolyline: Polyline = { - ...activePolyline, - path: appendCoordinateToPath(activePolyline.path, firstLayerInfos.coordinate, appendLocation), - }; - setPolylines((prevPolylines) => - prevPolylines.map((polyline) => (polyline.id === activePolyline.id ? newPolyline : polyline)) - ); + if (props.editingMode === PolylineEditingMode.DRAW) { + const newPolyline: Polyline = { + ...activePolyline, + path: appendCoordinateToPath( + activePolyline.path, + firstLayerInfos.coordinate, + appendLocation + ), + }; + setPolylines((prevPolylines) => + prevPolylines.map((polyline) => + polyline.id === activePolyline.id ? newPolyline : polyline + ) + ); - setReferencePathPointIndex( - appendLocation === PathAppendLocation.END ? activePolyline.path.length : 0 - ); + setReferencePathPointIndex( + appendLocation === PathAppendLocation.END ? activePolyline.path.length : 0 + ); + } } } }, [ - editingActive, + props.editingMode, activePolylineId, appendLocation, polylines, referencePathPointIndex, draggedPointIndex, - onEditingDone, + onEditingModeChange, ] ); @@ -450,6 +478,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita id: "polylines-layer", polylines: polylines.filter((polyline) => polyline.id !== activePolylineId), selectedPolylineId: selectedPolylineId ?? undefined, + hoverable: props.editingMode === PolylineEditingMode.IDLE, }), ]; @@ -460,7 +489,8 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita id: "editable-polylines-layer", polyline: activePolyline, mouseHoverPoint: hoverPoint ?? undefined, - referencePathPointIndex, + referencePathPointIndex: + props.editingMode === PolylineEditingMode.DRAW ? referencePathPointIndex : undefined, onDragStart, onDragEnd, }) @@ -489,13 +519,16 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita return `url(${removePathIcon}), crosshair`; } - if (activePolylineId && referencePathPointIndex !== undefined) { + if ( + (activePolylineId && referencePathPointIndex !== undefined) || + (!activePolylineId && props.editingMode === PolylineEditingMode.DRAW) + ) { return `url(${setPathPointIcon}), crosshair`; } return "default"; }, - [cursorIcon, activePolylineId, referencePathPointIndex] + [cursorIcon, activePolylineId, referencePathPointIndex, props.editingMode] ); return { @@ -506,6 +539,8 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita disableCameraInteraction: draggedPointIndex !== null, contextMenuItems, cursorPosition, + activePolylineId, + polylines: changedPolylines, }; } diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts index 1499a606f..cf01cf7d2 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts @@ -7,6 +7,14 @@ export type Polyline = { path: number[][]; }; +export enum PolylineEditingMode { + DRAW = "draw", + ADD_POINT = "add_point", + REMOVE_POINT = "remove_point", + NONE = "none", + IDLE = "idle", +} + export type ContextMenuItem = { icon?: React.ReactNode; label: string; From 5e6815ae8a18b1c11f60bfbbd024bfecaad5f856 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 22 Jan 2025 16:42:55 +0100 Subject: [PATCH 14/97] Removed icons --- frontend/src/assets/add_path.ico | Bin 4286 -> 0 bytes frontend/src/assets/add_path.ico:Zone.Identifier | 4 ---- frontend/src/assets/continue_path.ico | Bin 4286 -> 0 bytes .../src/assets/continue_path.ico:Zone.Identifier | 4 ---- frontend/src/assets/remove_path.ico | Bin 4286 -> 0 bytes .../src/assets/remove_path.ico:Zone.Identifier | 4 ---- 6 files changed, 12 deletions(-) delete mode 100644 frontend/src/assets/add_path.ico delete mode 100644 frontend/src/assets/add_path.ico:Zone.Identifier delete mode 100644 frontend/src/assets/continue_path.ico delete mode 100644 frontend/src/assets/continue_path.ico:Zone.Identifier delete mode 100644 frontend/src/assets/remove_path.ico delete mode 100644 frontend/src/assets/remove_path.ico:Zone.Identifier diff --git a/frontend/src/assets/add_path.ico b/frontend/src/assets/add_path.ico deleted file mode 100644 index 85471e5b8d5f8bacab70a18fe14e6da9048d687f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmdT|+fSQi82|hayfSX%IF4g-nlM(FW=Eimxv5d7n{#%cX4cJkp&4kh3^%}zc*VTH z12A0{baY+}v*Anz2BP zM04}^|G5g&)6+1#T!3{ChoHK;HYoT7$6rCnhH%u@T=E-#e0&^JQ&abSer9F{g$1v! z3ZF5&V5-206GmLUYR1680Lb%2E~g9i>N_>6uW9dRpH?F$I}c%@kubh~5}#lCLipTY zX+8`L{El5a_h7^NFhobkVdswBC@Ojvjg8-8XlTeQxB2e2j4Z5Q7b-T>@8Mw^5fL6G zI`El``S6DEl-F5b_K;^bUqeiEyvWexgoZ>QE;a%C@}5BH`7*S%Sui;{35VlPyz};H z95)n#acAzOJ(?jpWk?a}O@4%!L}i3D}oy(=4byIo9R%z2J$ zY-~()pv{a0`+&ChZ!r`he}7OG<9X~Q1Gc25iw?x|)P>K5g|A^^V#4#Nd?%K_`A^;J z>b^ol!#C*fx8diXe!<1ckMQg>hw#YeWQixSH%7+b^5rW_u*o34!B8OnW6rIVTrL+( zrVEIT*@Rd%_Bq*ll$?DZw{JJWX0xNEr4{AnCg}C~*t;heHdQ}gME@}#n3JrXm5@`d zq4Vd;B|}iz)LzEgb~>Fn^xR?THyS?oUwHnA?0L%Z_j_k&*Fv8m zMx^Td+FJMAv{)=lVnh6+FCN3_=%~MtUgx^Hx>bK~75f?6OxBZJ` zoycbosIPbTtfr=B={>~d;yh$8rfz{G_OpzPOvy3MeBL!kB_-#)d|zDrp3JsDVpCRM zUq5u)w#yx%=yb8n%fZ1xweIp4dNAh+d22zOb2{C2b?zkK#*IdSxOuY?@o}5QC;Rgc zfH7JtvfJ!pzdm=L*vR=oU-RxIx!w}ET=!*z-Ts@3pC$3;{=IvDsCS#aGViYiIM;pQ zkBp4S?7eg6N7*JOXKrQX#enC!FMRTNFVp&YUEP-`EiDJ{)xjXv_tfP5|J@M`UH`9` K+W!H9_4_Xz;;keA diff --git a/frontend/src/assets/add_path.ico:Zone.Identifier b/frontend/src/assets/add_path.ico:Zone.Identifier deleted file mode 100644 index 4ea481bc6..000000000 --- a/frontend/src/assets/add_path.ico:Zone.Identifier +++ /dev/null @@ -1,4 +0,0 @@ -[ZoneTransfer] -ZoneId=3 -ReferrerUrl=https://cloudconvert.com/ -HostUrl=https://eu-central.storage.cloudconvert.com/tasks/59c77e3f-77e4-4f93-af94-9405785612a8/add_path.ico?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=cloudconvert-production%2F20250120%2Ffra%2Fs3%2Faws4_request&X-Amz-Date=20250120T144016Z&X-Amz-Expires=86400&X-Amz-Signature=68edd04f71aac995e34d7544ae364e4b35488bf55946b4437765960cb73a9901&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3D%22add_path.ico%22&response-content-type=image%2Fvnd.microsoft.icon&x-id=GetObject diff --git a/frontend/src/assets/continue_path.ico b/frontend/src/assets/continue_path.ico deleted file mode 100644 index 939acffc98bf94551a5dd791910f6ce4d4941232..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmd^D=~L8Y9RKwn^d?j8V>!gQ|NqK0TjjXIDrNXqMqcU*_50Z=d7)`QG1$Rg|a1 zpOcfqPvzp%in2~o6i+e4l(J0>&Px(&u0Ez=notz1GL4UqV`O9mM&r~fBRyh2Gc$w9 z$w^F2O`)LRGsMRy;QI9{xq)-5O;uGtBQx_9?%wUe@#D$Zu-*=yLXm{q^70`zFHUz25@d6 zdN}wHUw!#Ca&q#pJ33b4Q3msHQ+Xogja2047vRSqDwbsfgW)fPZQlV~8waR_4%@ed zBO~J@(8sj(Tsu;>;9xb@KWhsIduI!-8J|!3W!*D2NIP+I-r0rp;ha`mE^Oy%;YGgm zp@+Md(D5bYoXbUBoemQd6Brm6l(x#u_!x0qa_dY=XgMQ22 zC%rIN%Y@I5y_Sg0{y|a(>drVa^W^vBKY@xiaQxXAXPeXT}}l zhqa}!@N?*dkK5ZjaOY0FsO`o@SrTt>aBx}mgL-FWorcQQR4dFrHa|_WI7wN&z5Q_b z&`~rrG%kw0(uKyxCiwbno)=%lo*Qp-WuZ+lBJ=&< zau7c^H-AxnD_x-ee!h?NCmykH-1r5IpXjJqX?wE`Sj*l{e;4ex)}W@QX88LBTJ&dr zB_<}z+#Vht#)0@lvi6zrxt?(FFt}z7YHn_^)EQzp3)`2Ln*6KN=^pb9@sA!kCUeai z`0R^;qHogf^lhm61(9RzlC?(kK+c(U=lhn>7g1YVXANk{qqVgSL83+ypSrR3vzGMt z_hav#eXhn)mug1@xrxhru#DQ&7K~cq)lRD_DCPC1~z%>yYy23sL0(?N8YVyYu>$Ntk+mC z))&%2ukRN4Tcp3ae(&Dj2oH+@`+d2UV|^j~fq?F{QPE`)*>Ywe54nqnf7(Q_vSa-H}kcUv_QNR3W?sO zM++rskt9hWf`v$0Cz#|+V$DCo#x{W@VV>qAFiQjm2L~}SGBV3ubF(}?K8`|d5i&DR zm3&$aiO3=zB)S zDGUt_IY0W}*8>9sC@Xt{ii(%$>@?!b=dZY3dAKQ?30TuVh`Q8p3gN$ zaVGW_c_x2DmxW{Jj@>9Kx&^)d1KQg=(9rOWb1erx`F-WeRRs74Psy>p-fXrg*p~3{ zC>%a?6b3`19MG(zv9Sq}5i6!)JJ*}DEfkXy(P!i$gTa7^@aQS{R9`VGmAv2owS(~U z^KZ%p5Ar`M@~{7dN7z+WZ}H+q1vaal`K~CSV&p zFQdI}u~@Kw-y!aOc6?etc;GOv$zecqbBm+T5Qd+KeQBvJzk0oXT5bsc=#k^-@9&ob z9xW}cBHpXmf68r~YK_=q?W|iXW+2JzzSDJl+*;Jt)yslSN?Kdn5F4XpeDaNE6!qZV z-d^n3zKi<+;Zt2uo$7SYWy2;#ZEfvX6{}+X)C0=Pb*#U-x`ulX;S$q4q+U$6$w}1D zl*%}+F`D^w)*#Bwy*?%OIXRa>^FeOv=;(xMbpoFovM=WFeD3P%68kRI(StRbbdYQ= zh~}J1l_2I$C|Xa?)Bb}`%)^zsbTCbI_ z)?N8vGJO~L@tkj3|M~M5k`j}7-anJM)?MNE_4V=W{rK?{FB2xs+~VTfve&vRe3H?* l%-+w-${r&xuK;web_db^9vODe?+$nP>kdr&{{Zg#`~yBv+}HpB diff --git a/frontend/src/assets/remove_path.ico:Zone.Identifier b/frontend/src/assets/remove_path.ico:Zone.Identifier deleted file mode 100644 index 86089d6d7..000000000 --- a/frontend/src/assets/remove_path.ico:Zone.Identifier +++ /dev/null @@ -1,4 +0,0 @@ -[ZoneTransfer] -ZoneId=3 -ReferrerUrl=https://cloudconvert.com/ -HostUrl=https://eu-central.storage.cloudconvert.com/tasks/67f45301-df20-46b1-b659-ce98e2d6ecf9/remove_path.ico?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=cloudconvert-production%2F20250120%2Ffra%2Fs3%2Faws4_request&X-Amz-Date=20250120T144015Z&X-Amz-Expires=86400&X-Amz-Signature=3c1fd47ed0dc0f26b79d3d21d5543bb4efb6ce4045776fe9b0ab8b9f30f5e436&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3D%22remove_path.ico%22&response-content-type=image%2Fvnd.microsoft.icon&x-id=GetObject From fdc6cfb624d0c9b7baee9dbf787d6b0b2804f8cf Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 22 Jan 2025 17:16:35 +0100 Subject: [PATCH 15/97] wip --- frontend/src/lib/components/Slider/slider.tsx | 24 ++--- .../RealizationGridLayer.ts | 20 ++-- .../RealizationGridSettingsContext.ts | 24 ++--- .../RealizationGridLayer/types.ts | 6 +- .../layerManagerComponentWrapper.tsx | 10 +- .../deckGlLayers/PolylinesLayer.ts | 2 +- .../editablePolylinesHook.tsx | 3 +- ...Setting.tsx => GridLayerIRangeSetting.tsx} | 52 +++++------ ...Setting.tsx => GridLayerJRangeSetting.tsx} | 52 +++++------ .../GridLayerKRangeSetting.tsx | 93 +++++++++++++++++++ .../LayerFramework/settings/settingsTypes.ts | 5 +- 11 files changed, 181 insertions(+), 110 deletions(-) rename frontend/src/modules/_shared/LayerFramework/settings/implementations/{GridLayerISetting.tsx => GridLayerIRangeSetting.tsx} (54%) rename frontend/src/modules/_shared/LayerFramework/settings/implementations/{GridLayerJSetting.tsx => GridLayerJRangeSetting.tsx} (54%) create mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting.tsx diff --git a/frontend/src/lib/components/Slider/slider.tsx b/frontend/src/lib/components/Slider/slider.tsx index 4e52efab6..78d21a00b 100644 --- a/frontend/src/lib/components/Slider/slider.tsx +++ b/frontend/src/lib/components/Slider/slider.tsx @@ -58,12 +58,12 @@ export const Slider = React.forwardRef((props: SliderProps, ref: React.Forwarded const activeThumb = Array.from(elements).findIndex( (element) => element === - document - .elementsFromPoint(e.clientX, e.clientY) - .filter((el) => el.classList.contains("MuiSlider-thumb")) - .at(0) ?? - elements[0] ?? - elements.item(0) + (document + .elementsFromPoint(e.clientX, e.clientY) + .filter((el) => el.classList.contains("MuiSlider-thumb")) + .at(0) ?? + elements[0] ?? + elements.item(0)) ); if (activeThumb >= 0) { setCurrentlyActiveThumb(activeThumb); @@ -96,12 +96,12 @@ export const Slider = React.forwardRef((props: SliderProps, ref: React.Forwarded const activeThumb = Array.from(elements).findIndex( (element) => element === - document - .elementsFromPoint(e.clientX, e.clientY) - .filter((el) => el.classList.contains("MuiSlider-thumb")) - .at(0) ?? - elements[0] ?? - elements.item(0) + (document + .elementsFromPoint(e.clientX, e.clientY) + .filter((el) => el.classList.contains("MuiSlider-thumb")) + .at(0) ?? + elements[0] ?? + elements.item(0)) ); if (activeThumb >= 0) { setCurrentlyActiveThumb(activeThumb); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts index b9540bf1d..3225d70d4 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -113,21 +113,21 @@ export class RealizationGridLayer if (timeOrInterval === "NO_TIME") { timeOrInterval = null; } - let availableDimensions = settings[SettingType.GRID_LAYER_K].getDelegate().getAvailableValues(); + let availableDimensions = settings[SettingType.GRID_LAYER_K_RANGE].getDelegate().getAvailableValues(); if (!availableDimensions.length || availableDimensions[0] === null) { availableDimensions = [0, 0, 0]; } - const layerIIndex = settings[SettingType.GRID_LAYER_I].getDelegate().getValue(); - const iMin = layerIIndex || 0; - const iMax = availableDimensions[0] || 0; + const layerIRange = settings[SettingType.GRID_LAYER_I_RANGE].getDelegate().getValue(); + const iMin = layerIRange?.[0] ?? 0; + const iMax = layerIRange?.[1] ?? 0; - const layerJIndex = settings[SettingType.GRID_LAYER_J].getDelegate().getValue(); - const jMin = layerJIndex || 0; - const jMax = availableDimensions[1] || 0; + const layerJRange = settings[SettingType.GRID_LAYER_J_RANGE].getDelegate().getValue(); + const jMin = layerJRange?.[0] ?? 0; + const jMax = layerJRange?.[1] ?? 0; - const layerKIndex = settings[SettingType.GRID_LAYER_K].getDelegate().getValue(); - const kMin = layerKIndex || 0; - const kMax = availableDimensions[2] || 0; + const layerKRange = settings[SettingType.GRID_LAYER_K_RANGE].getDelegate().getValue(); + const kMin = layerKRange?.[0] ?? 0; + const kMax = layerKRange?.[1] ?? 0; const queryKey = [ "gridParameter", diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts index 3013a27cc..eb83f35a6 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts @@ -4,9 +4,9 @@ import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerMan import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; import { GridAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting"; -import { GridLayerISetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerISetting"; -import { GridLayerJSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerJSetting"; -import { GridLayerKSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKSetting"; +import { GridLayerIRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting"; +import { GridLayerJRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting"; +import { GridLayerKRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting"; import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; @@ -27,9 +27,9 @@ export class RealizationGridSettingsContext implements SettingsContext { + availableSettingsUpdater(SettingType.GRID_LAYER_I_RANGE, ({ getLocalSetting, getHelperDependency }) => { const gridName = getLocalSetting(SettingType.GRID_NAME); const data = getHelperDependency(realizationGridDataDep); @@ -154,7 +154,7 @@ export class RealizationGridSettingsContext implements SettingsContext { + availableSettingsUpdater(SettingType.GRID_LAYER_J_RANGE, ({ getLocalSetting, getHelperDependency }) => { const gridName = getLocalSetting(SettingType.GRID_NAME); const data = getHelperDependency(realizationGridDataDep); @@ -173,7 +173,7 @@ export class RealizationGridSettingsContext implements SettingsContext { + availableSettingsUpdater(SettingType.GRID_LAYER_K_RANGE, ({ getLocalSetting, getHelperDependency }) => { const gridName = getLocalSetting(SettingType.GRID_NAME); const data = getHelperDependency(realizationGridDataDep); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts index 6ba7a6f00..bf195a92d 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts @@ -6,9 +6,9 @@ export type RealizationGridSettings = { [SettingType.REALIZATION]: number | null; [SettingType.GRID_ATTRIBUTE]: string | null; [SettingType.GRID_NAME]: string | null; - [SettingType.GRID_LAYER_I]: number | null; - [SettingType.GRID_LAYER_J]: number | null; - [SettingType.GRID_LAYER_K]: number | null; + [SettingType.GRID_LAYER_I_RANGE]: [number, number] | null; + [SettingType.GRID_LAYER_J_RANGE]: [number, number] | null; + [SettingType.GRID_LAYER_K_RANGE]: [number, number] | null; [SettingType.TIME_OR_INTERVAL]: string | null; [SettingType.SHOW_GRID_LINES]: boolean; }; diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index ddf4085e8..bca2e4277 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -9,7 +9,6 @@ import { MenuButton } from "@lib/components/MenuButton"; import { MenuHeading } from "@lib/components/MenuHeading"; import { MenuItem } from "@lib/components/MenuItem"; import { ObservedSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer"; -import { RealizationGridLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer"; import { RealizationSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { StatisticalSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; import { PreferredViewLayout } from "@modules/2DViewer/types"; @@ -25,8 +24,6 @@ import { SettingsGroup } from "@modules/_shared/LayerFramework/framework/Setting import { SharedSetting } from "@modules/_shared/LayerFramework/framework/SharedSetting/SharedSetting"; import { View } from "@modules/_shared/LayerFramework/framework/View/View"; import { Group, Item, instanceofGroup, instanceofLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; -import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { Dropdown } from "@mui/base"; @@ -41,6 +38,7 @@ import { import { useAtom } from "jotai"; +import { RealizationGridLayer } from "../../LayerFramework/customLayerImplementations/RealizationGridLayer"; import { preferredViewLayoutAtom } from "../atoms/baseAtoms"; export type LayerManagerComponentWrapperProps = { @@ -78,12 +76,6 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper case "color-scale": groupDelegate.prependChild(new ColorScale("Color scale", props.layerManager)); return; - case "drilled-wellbore-trajectories": - groupDelegate.insertChild(new DrilledWellTrajectoriesLayer(props.layerManager), numSharedSettings); - return; - case "drilled-wellbore-picks": - groupDelegate.insertChild(new DrilledWellborePicksLayer(props.layerManager), numSharedSettings); - return; case "realization-grid": groupDelegate.insertChild(new RealizationGridLayer(props.layerManager), numSharedSettings); return; diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts index dd1d01089..d5613e101 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts @@ -84,7 +84,7 @@ export class PolylinesLayer extends CompositeLayer { } } - if (hoveredPolylineIndex !== null && this.props.polylines[hoveredPolylineIndex]) { + if (hoveredPolylineIndex !== null && this.props.polylines[hoveredPolylineIndex] && this.props.hoverable) { layers.push( new PathLayer({ id: `hovered`, diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx index ca247ccae..d36a7e3a7 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx @@ -477,7 +477,8 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita new PolylinesLayer({ id: "polylines-layer", polylines: polylines.filter((polyline) => polyline.id !== activePolylineId), - selectedPolylineId: selectedPolylineId ?? undefined, + selectedPolylineId: + props.editingMode === PolylineEditingMode.NONE ? undefined : selectedPolylineId ?? undefined, hoverable: props.editingMode === PolylineEditingMode.IDLE, }), ]; diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerISetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting.tsx similarity index 54% rename from frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerISetting.tsx rename to frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting.tsx index c20464198..0a9848ac5 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerISetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting.tsx @@ -1,15 +1,15 @@ import React from "react"; -import { Dropdown, DropdownOption } from "@lib/components/Dropdown"; +import { Slider } from "@lib/components/Slider"; import { SettingDelegate } from "../../delegates/SettingDelegate"; import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; import { SettingRegistry } from "../SettingRegistry"; import { SettingType } from "../settingsTypes"; -type ValueType = number | null; +type ValueType = [number, number] | null; -export class GridLayerISetting implements Setting { +export class GridLayerIRangeSetting implements Setting { private _delegate: SettingDelegate; constructor() { @@ -17,7 +17,7 @@ export class GridLayerISetting implements Setting { } getType(): SettingType { - return SettingType.GRID_LAYER_I; + return SettingType.GRID_LAYER_I_RANGE; } getLabel(): string { @@ -44,7 +44,7 @@ export class GridLayerISetting implements Setting { return false; } - return value >= min && value <= max; + return value[0] >= min && value[0] <= max; } fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { @@ -60,42 +60,34 @@ export class GridLayerISetting implements Setting { } if (currentValue === null) { - return min; + return [min, max]; } - if (currentValue < min) { - return min; - } - - if (currentValue > max) { - return max; - } - - return currentValue; + return [Math.max(currentValue[0], min), Math.min(currentValue[1], max)]; } makeComponent(): (props: SettingComponentProps) => React.ReactNode { - return function Ensemble(props: SettingComponentProps) { - const iRange = props.availableValues ? Array.from({ length: props.availableValues[0] }, (_, i) => i) : []; + return function IRangeSlider(props: SettingComponentProps) { + function handleChange(_: any, value: number | number[]) { + if (!Array.isArray(value)) { + return; + } - const options: DropdownOption[] = iRange.map((value) => { - return { - value: value.toString(), - label: value === null ? "None" : value.toString(), - }; - }); + props.onValueChange([value[0], value[1]]); + } return ( - props.onValueChange(parseInt(val))} - disabled={props.isOverridden} - showArrows + ); }; } } -SettingRegistry.registerSetting(GridLayerISetting); +SettingRegistry.registerSetting(GridLayerIRangeSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting.tsx similarity index 54% rename from frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJSetting.tsx rename to frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting.tsx index 3e0a2bd57..f6804db02 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting.tsx @@ -1,15 +1,15 @@ import React from "react"; -import { Dropdown, DropdownOption } from "@lib/components/Dropdown"; +import { Slider } from "@lib/components/Slider"; import { SettingDelegate } from "../../delegates/SettingDelegate"; import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; import { SettingRegistry } from "../SettingRegistry"; import { SettingType } from "../settingsTypes"; -type ValueType = number | null; +type ValueType = [number, number] | null; -export class GridLayerJSetting implements Setting { +export class GridLayerJRangeSetting implements Setting { private _delegate: SettingDelegate; constructor() { @@ -17,7 +17,7 @@ export class GridLayerJSetting implements Setting { } getType(): SettingType { - return SettingType.GRID_LAYER_J; + return SettingType.GRID_LAYER_J_RANGE; } getLabel(): string { @@ -44,7 +44,7 @@ export class GridLayerJSetting implements Setting { return false; } - return value >= min && value <= max; + return value[0] >= min && value[0] <= max; } fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { @@ -60,42 +60,34 @@ export class GridLayerJSetting implements Setting { } if (currentValue === null) { - return min; + return [min, max]; } - if (currentValue < min) { - return min; - } - - if (currentValue > max) { - return max; - } - - return currentValue; + return [Math.max(currentValue[0], min), Math.min(currentValue[1], max)]; } makeComponent(): (props: SettingComponentProps) => React.ReactNode { - return function Ensemble(props: SettingComponentProps) { - const iRange = props.availableValues ? Array.from({ length: props.availableValues[1] }, (_, i) => i) : []; + return function JRangeSlider(props: SettingComponentProps) { + function handleChange(_: any, value: number | number[]) { + if (!Array.isArray(value)) { + return; + } - const options: DropdownOption[] = iRange.map((value) => { - return { - value: value.toString(), - label: value === null ? "None" : value.toString(), - }; - }); + props.onValueChange([value[0], value[1]]); + } return ( - props.onValueChange(parseInt(val))} - disabled={props.isOverridden} - showArrows + ); }; } } -SettingRegistry.registerSetting(GridLayerJSetting); +SettingRegistry.registerSetting(GridLayerJRangeSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting.tsx new file mode 100644 index 000000000..bd2ac0aaa --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting.tsx @@ -0,0 +1,93 @@ +import React from "react"; + +import { Slider } from "@lib/components/Slider"; + +import { SettingDelegate } from "../../delegates/SettingDelegate"; +import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; +import { SettingRegistry } from "../SettingRegistry"; +import { SettingType } from "../settingsTypes"; + +type ValueType = [number, number] | null; + +export class GridLayerKRangeSetting implements Setting { + private _delegate: SettingDelegate; + + constructor() { + this._delegate = new SettingDelegate(null, this); + } + + getType(): SettingType { + return SettingType.GRID_LAYER_K_RANGE; + } + + getLabel(): string { + return "Grid layer K"; + } + + getDelegate(): SettingDelegate { + return this._delegate; + } + + isValueValid(availableValues: AvailableValuesType, value: ValueType): boolean { + if (value === null) { + return false; + } + + if (availableValues.length < 3) { + return false; + } + + const min = 0; + const max = availableValues[2]; + + if (max === null) { + return false; + } + + return value[0] >= min && value[0] <= max; + } + + fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { + if (availableValues.length < 3) { + return null; + } + + const min = 0; + const max = availableValues[2]; + + if (max === null) { + return null; + } + + if (currentValue === null) { + return [min, max]; + } + + return [Math.max(currentValue[0], min), Math.min(currentValue[1], max)]; + } + + makeComponent(): (props: SettingComponentProps) => React.ReactNode { + return function KRangeSlider(props: SettingComponentProps) { + function handleChange(_: any, value: number | number[]) { + if (!Array.isArray(value)) { + return; + } + + props.onValueChange([value[0], value[1]]); + } + + return ( + + ); + }; + } +} + +SettingRegistry.registerSetting(GridLayerKRangeSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts index 5fe34db47..574f2e304 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts +++ b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts @@ -11,8 +11,9 @@ export enum SettingType { SMDA_WELLBORE_HEADERS = "smdaWellboreHeaders", GRID_NAME = "gridName", GRID_ATTRIBUTE = "gridAttribute", - GRID_LAYER_I = "gridLayerI", - GRID_LAYER_J = "gridLayerJ", + GRID_LAYER_I_RANGE = "gridLayerIRange", + GRID_LAYER_J_RANGE = "gridLayerJRange", + GRID_LAYER_K_RANGE = "gridLayerKRange", GRID_LAYER_K = "gridLayerK", SHOW_GRID_LINES = "showGridLines", } From 67d30614176c0a3b13d77e118c687feb365fb4c9 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 22 Jan 2025 23:42:24 +0100 Subject: [PATCH 16/97] fix --- frontend/package-lock.json | 105 +++++++++++++++++++------------------ frontend/package.json | 16 ++++-- 2 files changed, 65 insertions(+), 56 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 739dc7d20..850596f98 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,21 +8,27 @@ "name": "webviz", "version": "0.0.0", "dependencies": { + "@deck.gl/aggregation-layers": "^9.0.33", + "@deck.gl/core": "^9.0.33", + "@deck.gl/extensions": "^9.0.33", + "@deck.gl/geo-layers": "^9.0.33", + "@deck.gl/json": "^9.0.33", + "@deck.gl/layers": "^9.0.33", + "@deck.gl/mesh-layers": "^9.0.33", + "@deck.gl/react": "^9.0.33", "@equinor/eds-core-react": "^0.42.5", "@equinor/esv-intersection": "^3.0.10", "@headlessui/react": "^1.7.8", "@hey-api/client-axios": "^0.4.0", - "@luma.gl/engine": "^9.0.28", - "@luma.gl/webgl": "^9.0.28", "@mui/base": "^5.0.0-beta.3", "@mui/icons-material": "^5.14.9", "@tanstack/query-core": "^5.62.16", "@tanstack/react-query": "^5.63", "@tanstack/react-query-devtools": "^5.63", "@types/geojson": "^7946.0.14", - "@webviz/group-tree-plot": "^1.1.14", - "@webviz/subsurface-viewer": "^1.6.0", - "@webviz/well-completions-plot": "^1.5.11", + "@webviz/group-tree-plot": "^1.3.24", + "@webviz/subsurface-viewer": "^1.6.1", + "@webviz/well-completions-plot": "^1.5.20", "@webviz/well-log-viewer": "^1.12.7", "animate.css": "^4.1.1", "axios": "^1.6.5", @@ -548,6 +554,7 @@ "version": "9.0.40", "resolved": "https://registry.npmjs.org/@deck.gl/aggregation-layers/-/aggregation-layers-9.0.40.tgz", "integrity": "sha512-tZ3NEDVlZnCnwbxdoB+qB184gSricnbcOZwkPHqNWk+2wadyd6Q0j9V9aZ9V6M5BGDn86DrOKPFygwmUl/jkwA==", + "license": "MIT", "dependencies": { "@luma.gl/constants": "~9.0.27", "@luma.gl/shadertools": "~9.0.27", @@ -565,6 +572,7 @@ "version": "9.0.40", "resolved": "https://registry.npmjs.org/@deck.gl/core/-/core-9.0.40.tgz", "integrity": "sha512-NfBXRTuiqhYE42dOz73XnKaO7YZH8qsR1/9U/6lSV/XKnfrFsbhhRUePz3Ik6S3GbVpOoqCDuTFf0i3Tj+pxyw==", + "license": "MIT", "dependencies": { "@loaders.gl/core": "^4.2.0", "@loaders.gl/images": "^4.2.0", @@ -588,6 +596,7 @@ "version": "9.0.40", "resolved": "https://registry.npmjs.org/@deck.gl/extensions/-/extensions-9.0.40.tgz", "integrity": "sha512-1lESbg4NLkXxonO5f6aEX9a1DP4d8Vd+YLz9O4SwBJeZQZSeo4d3iJuJPL3MyA4thqdaA1Q9Vi8gtD9Mru6asg==", + "license": "MIT", "dependencies": { "@luma.gl/constants": "~9.0.27", "@luma.gl/shadertools": "~9.0.27", @@ -603,6 +612,7 @@ "version": "9.0.40", "resolved": "https://registry.npmjs.org/@deck.gl/geo-layers/-/geo-layers-9.0.40.tgz", "integrity": "sha512-kIxryoyWzicqLvAImtuUSX7NEntSdMIjCq0DoDdvSfhE37R0FxDRc5Vse5fjg9/1nNshw7mEcD2KxUS8MI8ypA==", + "license": "MIT", "dependencies": { "@loaders.gl/3d-tiles": "^4.2.0", "@loaders.gl/gis": "^4.2.0", @@ -635,6 +645,7 @@ "version": "9.0.40", "resolved": "https://registry.npmjs.org/@deck.gl/json/-/json-9.0.40.tgz", "integrity": "sha512-GAkENkzNPjEcHOR2xZJkgvzDWlqRhaU2Um5rmSdvm2FVYlfEpIWkfIWzroT5YrVOJ8nGC4pOYBXo0SLyNOwM9Q==", + "license": "MIT", "dependencies": { "jsep": "^0.3.0" }, @@ -646,6 +657,7 @@ "version": "9.0.40", "resolved": "https://registry.npmjs.org/@deck.gl/layers/-/layers-9.0.40.tgz", "integrity": "sha512-7emTkPLVWeVuV5VPBTmHDJegkGH1i76GuvhHNXikper/Zwljf9QVUacPqk3or4lHBCzzlkeccCsRmTd3l+MBYQ==", + "license": "MIT", "dependencies": { "@loaders.gl/images": "^4.2.0", "@loaders.gl/schema": "^4.2.0", @@ -666,6 +678,7 @@ "version": "9.0.40", "resolved": "https://registry.npmjs.org/@deck.gl/mesh-layers/-/mesh-layers-9.0.40.tgz", "integrity": "sha512-aCYnxfXmTrod+YMC6cHN4fP/OBihkZi+hgllJkBDiKkFr83cktJJGfjruHYYyQQuBKUPI1J2LP6aUD5/vXO2MA==", + "license": "MIT", "dependencies": { "@loaders.gl/gltf": "^4.2.0", "@luma.gl/gltf": "~9.0.27", @@ -681,6 +694,7 @@ "version": "9.0.40", "resolved": "https://registry.npmjs.org/@deck.gl/react/-/react-9.0.40.tgz", "integrity": "sha512-NiLgIMbgcsukDVKBNwyoE4e6Xtb43uR1WFSpfI2ZcL85BJMfzy7YWsSjJCN5jTdovMv7b9jkNxWWrKY702x3Tg==", + "license": "MIT", "peerDependencies": { "@deck.gl/core": "^9.0.0", "react": ">=16.3.0", @@ -2232,11 +2246,11 @@ } }, "node_modules/@luma.gl/engine": { - "version": "9.0.28", - "resolved": "https://registry.npmjs.org/@luma.gl/engine/-/engine-9.0.28.tgz", - "integrity": "sha512-EidAG/1Sgq0OeoyrebkcrC5TMvfK8ycIpSAjIRdi8hjwpHIoHFj5P3MWeApTZ1HV0DroxChzrxRCHYylI8svPQ==", + "version": "9.0.27", + "resolved": "https://registry.npmjs.org/@luma.gl/engine/-/engine-9.0.27.tgz", + "integrity": "sha512-O4e7RbIjBJX5WLs8HJLjpccYEkcans4pz8+TI8Y7BO7gDq9ZbEASbVd5CT53jFLfTjnRuqAOpElfaXwQ/B7oWg==", "dependencies": { - "@luma.gl/shadertools": "9.0.28", + "@luma.gl/shadertools": "9.0.27", "@math.gl/core": "^4.0.0", "@probe.gl/log": "^4.0.2", "@probe.gl/stats": "^4.0.2" @@ -2245,19 +2259,6 @@ "@luma.gl/core": "^9.0.0" } }, - "node_modules/@luma.gl/engine/node_modules/@luma.gl/shadertools": { - "version": "9.0.28", - "resolved": "https://registry.npmjs.org/@luma.gl/shadertools/-/shadertools-9.0.28.tgz", - "integrity": "sha512-FFh9udQTcPOTGpMKjYbCqZ7M6SLiaER93afCpQoYT6i36bPjTX4WX51ijFxA1ABJyvsZAo4BLR54tF++jNxyEQ==", - "dependencies": { - "@math.gl/core": "^4.0.0", - "@math.gl/types": "^4.0.0", - "wgsl_reflect": "^1.0.1" - }, - "peerDependencies": { - "@luma.gl/core": "^9.0.0" - } - }, "node_modules/@luma.gl/gltf": { "version": "9.0.27", "resolved": "https://registry.npmjs.org/@luma.gl/gltf/-/gltf-9.0.27.tgz", @@ -3669,22 +3670,22 @@ } }, "node_modules/@probe.gl/env": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@probe.gl/env/-/env-4.0.9.tgz", - "integrity": "sha512-AOmVMD0/j78mX+k4+qX7ZhE0sY9H+EaJgIO6trik0BwV6VcrwxTGCGFAeuRsIGhETDnye06tkLXccYatYxAYwQ==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@probe.gl/env/-/env-4.1.0.tgz", + "integrity": "sha512-5ac2Jm2K72VCs4eSMsM7ykVRrV47w32xOGMvcgqn8vQdEMF9PRXyBGYEV9YbqRKWNKpNKmQJVi4AHM/fkCxs9w==" }, "node_modules/@probe.gl/log": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@probe.gl/log/-/log-4.0.9.tgz", - "integrity": "sha512-ebuZaodSRE9aC+3bVC7cKRHT8garXeT1jTbj1R5tQRqQYc9iGeT3iemVOHx5bN9Q6gAs/0j54iPI+1DvWMAW4A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@probe.gl/log/-/log-4.1.0.tgz", + "integrity": "sha512-r4gRReNY6f+OZEMgfWEXrAE2qJEt8rX0HsDJQXUBMoc+5H47bdB7f/5HBHAmapK8UydwPKL9wCDoS22rJ0yq7Q==", "dependencies": { - "@probe.gl/env": "4.0.9" + "@probe.gl/env": "4.1.0" } }, "node_modules/@probe.gl/stats": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@probe.gl/stats/-/stats-4.0.9.tgz", - "integrity": "sha512-Q9Xt/sJUQaMsbjRKjOscv2t7wXIymTrOEJ4a3da4FTCn7bkKvcdxdyFAQySCrtPxE+YZ5I5lXpWPgv9BwmpE1g==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@probe.gl/stats/-/stats-4.1.0.tgz", + "integrity": "sha512-EI413MkWKBDVNIfLdqbeNSJTs7ToBz/KVGkwi3D+dQrSIkRI2IYbWGAU3xX+D6+CI4ls8ehxMhNpUVMaZggDvQ==" }, "node_modules/@react-aria/breadcrumbs": { "version": "3.5.19", @@ -6755,9 +6756,10 @@ } }, "node_modules/@webviz/group-tree-plot": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@webviz/group-tree-plot/-/group-tree-plot-1.1.14.tgz", - "integrity": "sha512-3N5lhuQWn/lBvg0jQSN31Jp7hPjMhIygTYkCLGpFKGTZgPUB6RvNLKnl31EZJhFsQf9xhjJg/XfI5WifrTs+qw==", + "version": "1.3.24", + "resolved": "https://registry.npmjs.org/@webviz/group-tree-plot/-/group-tree-plot-1.3.24.tgz", + "integrity": "sha512-eO8PgGWLWColyk09p9GdRO6TQPByyFA8dCZm/VotGuLNopLOWxqXDlBW7cb/wiqZIzyhMrfHaQORha/YhaFgAA==", + "license": "MPL-2.0", "dependencies": { "d3": "^7.8.2", "lodash": "^4.17.21" @@ -6768,19 +6770,20 @@ } }, "node_modules/@webviz/subsurface-viewer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@webviz/subsurface-viewer/-/subsurface-viewer-1.6.0.tgz", - "integrity": "sha512-zSbjmN3+ommP9K6O/QIZjtvQm+6aWboFfsT1kx4S/viSohN4piUefJrRh4Eu2oxpEaZ6s5z+qY2kQkC4EDsMqw==", - "dependencies": { - "@deck.gl-community/editable-layers": "^9.0.3", - "@deck.gl/aggregation-layers": "^9.0.40", - "@deck.gl/core": "^9.0.40", - "@deck.gl/extensions": "^9.0.40", - "@deck.gl/geo-layers": "^9.0.40", - "@deck.gl/json": "^9.0.40", - "@deck.gl/layers": "^9.0.40", - "@deck.gl/mesh-layers": "^9.0.40", - "@deck.gl/react": "^9.0.40", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@webviz/subsurface-viewer/-/subsurface-viewer-1.6.1.tgz", + "integrity": "sha512-93xiQUhtg8JM92XzRChSCJM6U0rNoPn00A8ns4qXKru9ZGa6e47JtbE/hdIN9a5FVyPPbd8+WKkqdPCN1NstRw==", + "license": "MPL-2.0", + "dependencies": { + "@deck.gl-community/editable-layers": "9.0.3", + "@deck.gl/aggregation-layers": "9.0.40", + "@deck.gl/core": "9.0.40", + "@deck.gl/extensions": "9.0.40", + "@deck.gl/geo-layers": "9.0.40", + "@deck.gl/json": "9.0.40", + "@deck.gl/layers": "9.0.40", + "@deck.gl/mesh-layers": "9.0.40", + "@deck.gl/react": "9.0.40", "@emerson-eps/color-tables": "^0.4.85", "@equinor/eds-core-react": "^0.36.0", "@equinor/eds-icons": "^0.21.0", @@ -6906,9 +6909,9 @@ } }, "node_modules/@webviz/well-completions-plot": { - "version": "1.5.11", - "resolved": "https://registry.npmjs.org/@webviz/well-completions-plot/-/well-completions-plot-1.5.11.tgz", - "integrity": "sha512-NGAYNLd764izRfKszkhr88Bcdo/jGus4N8nd86LKjtKPCiTfYd4C7Ix82hAM50aWMAd9nMSzSKFXEUi4dNMzXg==", + "version": "1.5.20", + "resolved": "https://registry.npmjs.org/@webviz/well-completions-plot/-/well-completions-plot-1.5.20.tgz", + "integrity": "sha512-4+rKBO9T8iywJIoUFmHeThselxv1znBi1bGT+8q3WYkmn1aTFRg1xSCmGJZoU6IYvIX/wJBUfy0rf9AXB60GRg==", "license": "MPL-2.0", "dependencies": { "react-resize-detector": "^11.0.1", diff --git a/frontend/package.json b/frontend/package.json index 62da53bb5..949ba1840 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,21 +19,27 @@ "test:ct:ui": "playwright test -c playwright.ct.config.ts --ui" }, "dependencies": { + "@deck.gl/aggregation-layers": "^9.0.33", + "@deck.gl/core": "^9.0.33", + "@deck.gl/extensions": "^9.0.33", + "@deck.gl/geo-layers": "^9.0.33", + "@deck.gl/json": "^9.0.33", + "@deck.gl/layers": "^9.0.33", + "@deck.gl/mesh-layers": "^9.0.33", + "@deck.gl/react": "^9.0.33", "@equinor/eds-core-react": "^0.42.5", "@equinor/esv-intersection": "^3.0.10", "@headlessui/react": "^1.7.8", "@hey-api/client-axios": "^0.4.0", - "@luma.gl/engine": "^9.0.28", - "@luma.gl/webgl": "^9.0.28", "@mui/base": "^5.0.0-beta.3", "@mui/icons-material": "^5.14.9", "@tanstack/query-core": "^5.62.16", "@tanstack/react-query": "^5.63", "@tanstack/react-query-devtools": "^5.63", "@types/geojson": "^7946.0.14", - "@webviz/group-tree-plot": "^1.1.14", - "@webviz/subsurface-viewer": "^1.6.0", - "@webviz/well-completions-plot": "^1.5.11", + "@webviz/group-tree-plot": "^1.3.24", + "@webviz/subsurface-viewer": "^1.6.1", + "@webviz/well-completions-plot": "^1.5.20", "@webviz/well-log-viewer": "^1.12.7", "animate.css": "^4.1.1", "axios": "^1.6.5", From c401f23c6be0c6c30a6956c80960d99a6025587c Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Thu, 23 Jan 2025 11:25:37 +0100 Subject: [PATCH 17/97] realization surface --- .../RealizationGridLayer.ts | 2 +- .../RealizationSurfaceLayer.ts | 130 +++++++++++++++ .../RealizationSurfaceSettingsContext.ts | 152 ++++++++++++++++++ .../RealizationSurfaceLayer/index.ts | 2 + .../RealizationSurfaceLayer/types.ts | 10 ++ .../layerManagerComponentWrapper.tsx | 16 +- .../view/components/LayersWrapper.tsx | 2 +- .../3DViewerNew/view/utils/layerFactory.ts | 49 +++++- 8 files changed, 358 insertions(+), 5 deletions(-) create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts index 3225d70d4..19f22a56a 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -38,7 +38,7 @@ export class RealizationGridLayer private _itemDelegate: ItemDelegate; constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Realization Grid layer", layerManager); + this._itemDelegate = new ItemDelegate("Realization Grid", layerManager); this._layerDelegate = new LayerDelegate( this, layerManager, diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts new file mode 100644 index 000000000..da2c1f95c --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts @@ -0,0 +1,130 @@ +import { SurfaceDataPng_api, SurfaceTimeType_api } from "@api"; +import { getSurfaceDataOptions } from "@api"; +import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; +import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; +import { FullSurfaceAddress, SurfaceAddressBuilder } from "@modules/_shared/Surface"; +import { SurfaceDataFloat_trans, transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; +import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; +import { QueryClient } from "@tanstack/react-query"; + +import { isEqual } from "lodash"; + +import { RealizationSurfaceSettingsContext } from "./RealizationSurfaceSettingsContext"; +import { RealizationSurfaceSettings } from "./types"; + +export class RealizationSurfaceLayer + implements Layer +{ + private _layerDelegate: LayerDelegate; + private _itemDelegate: ItemDelegate; + + constructor(layerManager: LayerManager) { + this._itemDelegate = new ItemDelegate("Realization Surface", layerManager); + this._layerDelegate = new LayerDelegate( + this, + layerManager, + new RealizationSurfaceSettingsContext(layerManager), + LayerColoringType.COLORSCALE + ); + } + + getSettingsContext() { + return this._layerDelegate.getSettingsContext(); + } + + getItemDelegate(): ItemDelegate { + return this._itemDelegate; + } + + getLayerDelegate(): LayerDelegate { + return this._layerDelegate; + } + + doSettingsChangesRequireDataRefetch( + prevSettings: RealizationSurfaceSettings, + newSettings: RealizationSurfaceSettings + ): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeBoundingBox(): BoundingBox | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return { + x: [data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.max_x], + y: [data.transformed_bbox_utm.min_y, data.transformed_bbox_utm.max_y], + z: [0, 0], + }; + } + + makeValueRange(): [number, number] | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return [data.value_min, data.value_max]; + } + + fetchData(queryClient: QueryClient): Promise { + let surfaceAddress: FullSurfaceAddress | null = null; + const addrBuilder = new SurfaceAddressBuilder(); + + const settings = this.getSettingsContext().getDelegate().getSettings(); + const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); + const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); + const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); + const attribute = settings[SettingType.SURFACE_ATTRIBUTE].getDelegate().getValue(); + const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + + if (ensembleIdent && surfaceName && attribute && realizationNum !== null) { + addrBuilder.withEnsembleIdent(ensembleIdent); + addrBuilder.withName(surfaceName); + addrBuilder.withAttribute(attribute); + addrBuilder.withRealization(realizationNum); + + if (timeOrInterval !== SurfaceTimeType_api.NO_TIME) { + addrBuilder.withTimeOrInterval(timeOrInterval); + } + + surfaceAddress = addrBuilder.buildRealizationAddress(); + } + + const surfAddrStr = surfaceAddress ? encodeSurfAddrStr(surfaceAddress) : null; + + const queryKey = ["getSurfaceData", surfAddrStr, null, "float"]; + + this._layerDelegate.registerQueryKey(queryKey); + + const promise = queryClient + .fetchQuery({ + ...getSurfaceDataOptions({ + query: { + surf_addr_str: surfAddrStr ?? "", + data_format: "float", + resample_to_def_str: null, + }, + }), + }) + .then((data) => transformSurfaceData(data)); + + return promise; + } + + serializeState(): SerializedLayer { + return this._layerDelegate.serializeState(); + } + + deserializeState(serializedState: SerializedLayer): void { + this._layerDelegate.deserializeState(serializedState); + } +} + +LayerRegistry.registerLayer(RealizationSurfaceLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts new file mode 100644 index 000000000..9829608da --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts @@ -0,0 +1,152 @@ +import { SurfaceTimeType_api } from "@api"; +import { getRealizationSurfacesMetadataOptions } from "@api"; +import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; +import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; +import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; +import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; +import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +import { RealizationSurfaceSettings } from "./types"; + +export class RealizationSurfaceSettingsContext implements SettingsContext { + private _contextDelegate: SettingsContextDelegate; + + constructor(layerManager: LayerManager) { + this._contextDelegate = new SettingsContextDelegate< + RealizationSurfaceSettings, + keyof RealizationSurfaceSettings + >(this, layerManager, { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.REALIZATION]: new RealizationSetting(), + [SettingType.SURFACE_ATTRIBUTE]: new SurfaceAttributeSetting(), + [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + }); + } + + getDelegate(): SettingsContextDelegate { + return this._contextDelegate; + } + + getSettings() { + return this._contextDelegate.getSettings(); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + queryClient, + }: DefineDependenciesArgs) { + availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); + + if (!ensembleIdent) { + return []; + } + + const realizations = realizationFilterFunc(ensembleIdent); + + return [...realizations]; + }); + + const realizationSurfaceMetadataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + + if (!ensembleIdent) { + return null; + } + + return await queryClient.fetchQuery({ + ...getRealizationSurfacesMetadataOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + }, + signal: abortSignal, + }), + }); + }); + + availableSettingsUpdater(SettingType.SURFACE_ATTRIBUTE, ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSurfaceMetadataDep); + + if (!data) { + return []; + } + + const availableAttributes = [ + ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), + ]; + + return availableAttributes; + }); + + availableSettingsUpdater(SettingType.SURFACE_NAME, ({ getHelperDependency, getLocalSetting }) => { + const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const data = getHelperDependency(realizationSurfaceMetadataDep); + + if (!attribute || !data) { + return []; + } + + const availableSurfaceNames = [ + ...Array.from( + new Set( + data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name) + ) + ), + ]; + + return availableSurfaceNames; + }); + + availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); + const data = getHelperDependency(realizationSurfaceMetadataDep); + + if (!attribute || !surfaceName || !data) { + return []; + } + + const availableTimeOrIntervals: string[] = []; + const availableTimeTypes = [ + ...Array.from( + new Set( + data.surfaces + .filter((surface) => surface.attribute_name === attribute && surface.name === surfaceName) + .map((el) => el.time_type) + ) + ), + ]; + + if (availableTimeTypes.includes(SurfaceTimeType_api.NO_TIME)) { + availableTimeOrIntervals.push(SurfaceTimeType_api.NO_TIME); + } + if (availableTimeTypes.includes(SurfaceTimeType_api.TIME_POINT)) { + availableTimeOrIntervals.push(...data.time_points_iso_str); + } + if (availableTimeTypes.includes(SurfaceTimeType_api.INTERVAL)) { + availableTimeOrIntervals.push(...data.time_intervals_iso_str); + } + + return availableTimeOrIntervals; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts new file mode 100644 index 000000000..ed6642a0d --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts @@ -0,0 +1,2 @@ +export { RealizationSurfaceLayer } from "./RealizationSurfaceLayer"; +export { RealizationSurfaceSettingsContext } from "./RealizationSurfaceSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts new file mode 100644 index 000000000..c0225d599 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts @@ -0,0 +1,10 @@ +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +export type RealizationSurfaceSettings = { + [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; + [SettingType.REALIZATION]: number | null; + [SettingType.SURFACE_ATTRIBUTE]: string | null; + [SettingType.SURFACE_NAME]: string | null; + [SettingType.TIME_OR_INTERVAL]: string | null; +}; diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index bca2e4277..faa7022d8 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Icon } from "@equinor/eds-core-react"; -import { color_palette, grid_layer, settings, wellbore } from "@equinor/eds-icons"; +import { color_palette, grid_layer, settings, surface_layer, wellbore } from "@equinor/eds-icons"; import { WorkbenchSession } from "@framework/WorkbenchSession"; import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { Menu } from "@lib/components/Menu"; @@ -9,7 +9,6 @@ import { MenuButton } from "@lib/components/MenuButton"; import { MenuHeading } from "@lib/components/MenuHeading"; import { MenuItem } from "@lib/components/MenuItem"; import { ObservedSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer"; -import { RealizationSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { StatisticalSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; import { PreferredViewLayout } from "@modules/2DViewer/types"; import { EnsembleSetting } from "@modules//_shared/LayerFramework/settings/implementations/EnsembleSetting"; @@ -24,6 +23,7 @@ import { SettingsGroup } from "@modules/_shared/LayerFramework/framework/Setting import { SharedSetting } from "@modules/_shared/LayerFramework/framework/SharedSetting/SharedSetting"; import { View } from "@modules/_shared/LayerFramework/framework/View/View"; import { Group, Item, instanceofGroup, instanceofLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { Dropdown } from "@mui/base"; @@ -39,6 +39,7 @@ import { import { useAtom } from "jotai"; import { RealizationGridLayer } from "../../LayerFramework/customLayerImplementations/RealizationGridLayer"; +import { RealizationSurfaceLayer } from "../../LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { preferredViewLayoutAtom } from "../atoms/baseAtoms"; export type LayerManagerComponentWrapperProps = { @@ -79,6 +80,12 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper case "realization-grid": groupDelegate.insertChild(new RealizationGridLayer(props.layerManager), numSharedSettings); return; + case "realization-surface": + groupDelegate.insertChild(new RealizationSurfaceLayer(props.layerManager), numSharedSettings); + return; + case "drilled-wellbore-trajectories": + groupDelegate.insertChild(new DrilledWellTrajectoriesLayer(props.layerManager), numSharedSettings); + return; case "ensemble": groupDelegate.prependChild(new SharedSetting(new EnsembleSetting(), props.layerManager)); return; @@ -228,6 +235,11 @@ const LAYER_ACTIONS: LayersActionGroup[] = [ icon: , label: "Realization Grid", }, + { + identifier: "realization-surface", + icon: , + label: "Realization Surface", + }, ], }, ], diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index c49875dfe..e68ceffe7 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -157,7 +157,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { const layers = viewerLayers.toSorted((a, b) => b.position - a.position).map((layer) => layer.layer); layers.push(new PlaceholderLayer({ id: "placeholder" })); - layers.push(new AxesLayer({ id: "axes-layer", visible: true, ZIncreasingDownwards: true, bounds })); + layers.push(new AxesLayer({ id: "axes-layer", visible: true, ZIncreasingDownwards: false, bounds })); return (
diff --git a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts index 09b0ffa3e..3b5725cfe 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts @@ -6,13 +6,15 @@ import { GridMappedProperty_trans, GridSurface_trans } from "@modules/3DViewer/v import { Layer as LayerInterface } from "@modules/_shared/LayerFramework/interfaces"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; +import { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataTransforms"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; -import { Grid3DLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; +import { Grid3DLayer, MapLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; import { Rgb, parse } from "culori"; import { Feature } from "geojson"; import { RealizationGridLayer } from "../../LayerFramework/customLayerImplementations/RealizationGridLayer"; +import { RealizationSurfaceLayer } from "../../LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { AdvancedWellsLayer } from "../customDeckGlLayers/AdvancedWellsLayer"; import { WellBorePickLayerData, WellborePicksLayer } from "../customDeckGlLayers/WellborePicksLayer"; @@ -47,8 +49,53 @@ export function makeDeckGlLayer(layer: LayerInterface, colorScale?: Co colorScale ); } + if (layer instanceof RealizationSurfaceLayer) { + console.log(data); + return createSurfaceMeshLayer( + data, + layer.getItemDelegate().getId(), + layer.getItemDelegate().getName(), + colorScale + ); + } return null; } +export function createSurfaceMeshLayer( + layerData: SurfaceDataFloat_trans, + id: string, + name: string, + colorScale?: ColorScaleWithName, + showContours?: boolean | number[], + showGridLines?: boolean, + useSmoothShading?: boolean, + useMaterial?: boolean, + property_data?: Float32Array | null +): MapLayer { + return new MapLayer({ + "@@type": "MapLayer", + "@@typedArraySupport": true, + id: "mesh-layer", + name: name, + meshData: layerData.valuesFloat32Arr, + // propertiesData: layerData.valuesFloat32Arr, + frame: { + origin: [layerData.surface_def.origin_utm_x, layerData.surface_def.origin_utm_y], + count: [layerData.surface_def.npoints_x, layerData.surface_def.npoints_y], + increment: [layerData.surface_def.inc_x, layerData.surface_def.inc_y], + rotDeg: layerData.surface_def.rot_deg, + }, + valueRange: [layerData.value_min, layerData.value_max], + colorMapRange: [layerData.value_min, layerData.value_max], + isContoursDepth: true, + contours: [0, 10], + gridLines: true, + material: useMaterial, + smoothShading: useSmoothShading, + colorMapName: "Physics", + + colorMapFunction: makeColorMapFunction(colorScale, layerData.value_min, layerData.value_max), + }); +} function createWellPicksLayer(wellPicksDataApi: WellborePick_api[], id: string): WellborePicksLayer { const wellPicksData: WellBorePickLayerData[] = wellPicksDataApi.map((wellPick) => { return { From 770b71ea07e3a4efab76fd828c62869e0e13717e Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:31:03 +0100 Subject: [PATCH 18/97] Add seismic inline/crossline --- .../primary/routers/seismic/converters.py | 52 ++++++ .../primary/primary/routers/seismic/router.py | 82 ++++++++- .../primary/routers/seismic/schemas.py | 42 ++++- .../services/sumo_access/seismic_access.py | 9 +- .../services/sumo_access/seismic_types.py | 8 + .../services/vds_access/request_types.py | 25 ++- .../services/vds_access/response_types.py | 42 +++-- .../primary/services/vds_access/vds_access.py | 83 ++++++++- .../api/autogen/@tanstack/react-query.gen.ts | 42 +++++ frontend/src/api/autogen/sdk.gen.ts | 38 +++++ frontend/src/api/autogen/types.gen.ts | 150 ++++++++++++++++ .../RealizationSeismicCrosslineLayer.ts | 121 +++++++++++++ ...lizationSeismicCrosslineSettingsContext.ts | 156 +++++++++++++++++ .../RealizationSeismicCrosslineLayer/index.ts | 2 + .../RealizationSeismicCrosslineLayer/types.ts | 10 ++ .../RealizationSeismicInlineLayer.ts | 119 +++++++++++++ ...RealizationSeismicInlineSettingsContext.ts | 155 +++++++++++++++++ .../RealizationSeismicInlineLayer/index.ts | 2 + .../RealizationSeismicInlineLayer/types.ts | 10 ++ .../layerManagerComponentWrapper.tsx | 18 ++ .../settings/queries/queryDataTransforms.ts | 38 +++++ .../3DViewerNew/view/utils/layerFactory.ts | 65 ++++++- .../view/utils/seismicSliceUtils.ts | 160 ++++++++++++++++++ .../SeismicAttributeSetting.tsx | 53 ++++++ .../SeismicCrosslineSetting.tsx | 93 ++++++++++ .../implementations/SeismicInlineSetting.tsx | 93 ++++++++++ .../LayerFramework/settings/settingsTypes.ts | 3 + 27 files changed, 1646 insertions(+), 25 deletions(-) create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/index.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/index.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts create mode 100644 frontend/src/modules/3DViewerNew/settings/queries/queryDataTransforms.ts create mode 100644 frontend/src/modules/3DViewerNew/view/utils/seismicSliceUtils.ts create mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting.tsx create mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx create mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx diff --git a/backend_py/primary/primary/routers/seismic/converters.py b/backend_py/primary/primary/routers/seismic/converters.py index f679df274..9583fed50 100644 --- a/backend_py/primary/primary/routers/seismic/converters.py +++ b/backend_py/primary/primary/routers/seismic/converters.py @@ -2,11 +2,15 @@ import orjson import numpy as np +from numpy.typing import NDArray import xtgeo +from webviz_pkg.core_utils.b64 import b64_encode_float_array_as_float32 +from primary.services.vds_access.response_types import VdsSliceMetadata from . import schemas + def surface_to_float32_array(values: np.ndarray) -> List[float]: values = values.astype(np.float32) np.ma.set_fill_value(values, np.nan) @@ -48,3 +52,51 @@ def to_api_surface_data( mesh_data=orjson.dumps(float32_mesh).decode("utf-8"), # pylint: disable=maybe-no-member property_data=orjson.dumps(float32_property).decode("utf-8"), # pylint: disable=maybe-no-member ) + +def to_api_vds_inline_data( + flattened_slice_traces_array: NDArray[np.float32], metadata: VdsSliceMetadata) -> schemas.SeismicInlineData: + """ + Create API SeismicInlineData from VdsSliceMetadata and flattened slice traces array + """ + + return schemas.SeismicInlineData( + slice_traces_b64arr=b64_encode_float_array_as_float32(flattened_slice_traces_array), + start_utm_x=metadata.geospatial[0][0], + start_utm_y=metadata.geospatial[0][1], + end_utm_x=metadata.geospatial[1][0], + end_utm_y=metadata.geospatial[1][1], + crossline_min=metadata.y["min"], + crossline_max=metadata.y["max"], + crossline_no_samples=metadata.y["samples"], + z_min=metadata.x["min"], + z_max=metadata.x["max"], + z_samples=metadata.x["samples"], + z_unit=metadata.x["unit"], + value_min=np.nanmin(flattened_slice_traces_array), + value_max=np.nanmax(flattened_slice_traces_array), + + ) + +def to_api_vds_crossline_data( + flattened_slice_traces_array: NDArray[np.float32], metadata: VdsSliceMetadata) -> schemas.SeismicCrosslineData: + """ + Create API SeismicCrosslineData from VdsSliceMetadata and flattened slice traces array + """ + + return schemas.SeismicCrosslineData( + slice_traces_b64arr=b64_encode_float_array_as_float32(flattened_slice_traces_array), + start_utm_x=metadata.geospatial[0][0], + start_utm_y=metadata.geospatial[0][1], + end_utm_x=metadata.geospatial[1][0], + end_utm_y=metadata.geospatial[1][1], + inline_min=metadata.y["min"], + inline_max=metadata.y["max"], + inline_no_samples=metadata.y["samples"], + z_min=metadata.x["min"], + z_max=metadata.x["max"], + z_samples=metadata.x["samples"], + z_unit=metadata.x["unit"], + value_min=np.nanmin(flattened_slice_traces_array), + value_max=np.nanmax(flattened_slice_traces_array), + + ) \ No newline at end of file diff --git a/backend_py/primary/primary/routers/seismic/router.py b/backend_py/primary/primary/routers/seismic/router.py index 9995f8071..d5b159d9c 100644 --- a/backend_py/primary/primary/routers/seismic/router.py +++ b/backend_py/primary/primary/routers/seismic/router.py @@ -7,11 +7,12 @@ from primary.auth.auth_helper import AuthHelper from primary.services.sumo_access.seismic_access import SeismicAccess, VdsHandle from primary.services.utils.authenticated_user import AuthenticatedUser -from primary.services.vds_access.request_types import VdsCoordinates, VdsCoordinateSystem +from primary.services.vds_access.request_types import VdsCoordinates, VdsCoordinateSystem, VdsDirection from primary.services.vds_access.response_types import VdsMetadata from primary.services.vds_access.vds_access import VdsAccess from . import schemas +from . import converters LOGGER = logging.getLogger(__name__) @@ -36,6 +37,83 @@ async def get_seismic_cube_meta_list( except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc +@router.get("/get_inline_slice/") +async def get_inline_slice( + authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), + case_uuid: str = Query(description="Sumo case uuid"), + ensemble_name: str = Query(description="Ensemble name"), + realization_num: int = Query(description="Realization number"), + seismic_attribute: str = Query(description="Seismic cube attribute"), + time_or_interval_str: str = Query(description="Timestamp or timestep"), + observed: bool = Query(description="Observed or simulated"), + inline_no:int = Query(description="Inline number") +) -> schemas.SeismicInlineData: + """Get a seismic inline from a seismic cube. + """ + seismic_access = await SeismicAccess.from_case_uuid_async( + authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name + ) + + vds_handle: Optional[VdsHandle] = None + try: + vds_handle = await seismic_access.get_vds_handle_async( + realization=realization_num, + seismic_attribute=seismic_attribute, + time_or_interval_str=time_or_interval_str, + observed=observed, + ) + except ValueError as err: + raise HTTPException(status_code=404, detail=str(err)) from err + + if vds_handle is None: + raise HTTPException(status_code=404, detail="Vds handle not found") + + vds_access = VdsAccess(sas_token=vds_handle.sas_token, vds_url=vds_handle.vds_url) + + flattened_slice_traces_array,metadata = await vds_access.get_inline_slice( line_no=inline_no) + + return converters.to_api_vds_inline_data(flattened_slice_traces_array=flattened_slice_traces_array, + metadata=metadata + ) + +@router.get("/get_crossline_slice/") +async def get_crossline_slice( + authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), + case_uuid: str = Query(description="Sumo case uuid"), + ensemble_name: str = Query(description="Ensemble name"), + realization_num: int = Query(description="Realization number"), + seismic_attribute: str = Query(description="Seismic cube attribute"), + time_or_interval_str: str = Query(description="Timestamp or timestep"), + observed: bool = Query(description="Observed or simulated"), + crossline_no:int = Query(description="Crossline number") +) -> schemas.SeismicCrosslineData: + """Get a seismic crossline from a seismic cube. + """ + seismic_access = await SeismicAccess.from_case_uuid_async( + authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name + ) + + vds_handle: Optional[VdsHandle] = None + try: + vds_handle = await seismic_access.get_vds_handle_async( + realization=realization_num, + seismic_attribute=seismic_attribute, + time_or_interval_str=time_or_interval_str, + observed=observed, + ) + except ValueError as err: + raise HTTPException(status_code=404, detail=str(err)) from err + + if vds_handle is None: + raise HTTPException(status_code=404, detail="Vds handle not found") + + vds_access = VdsAccess(sas_token=vds_handle.sas_token, vds_url=vds_handle.vds_url) + + flattened_slice_traces_array,metadata = await vds_access.get_crossline_slice( line_no=crossline_no) + + return converters.to_api_vds_crossline_data(flattened_slice_traces_array=flattened_slice_traces_array, + metadata=metadata + ) @router.post("/get_seismic_fence/") async def post_get_seismic_fence( @@ -86,8 +164,8 @@ async def post_get_seismic_fence( coordinates=VdsCoordinates(polyline.x_points, polyline.y_points), coordinate_system=VdsCoordinateSystem.CDP, ) - meta: VdsMetadata = await vds_access.get_metadata_async() + if len(meta.axis) != 3: raise ValueError(f"Expected 3 axes, got {len(meta.axis)}") depth_axis_meta = meta.axis[2] diff --git a/backend_py/primary/primary/routers/seismic/schemas.py b/backend_py/primary/primary/routers/seismic/schemas.py index 98c2d1813..9a1dda2b5 100644 --- a/backend_py/primary/primary/routers/seismic/schemas.py +++ b/backend_py/primary/primary/routers/seismic/schemas.py @@ -1,4 +1,5 @@ from typing import List +from enum import StrEnum from pydantic import BaseModel from webviz_pkg.core_utils.b64 import B64FloatArray @@ -9,7 +10,14 @@ class SeismicCubeMeta(BaseModel): iso_date_or_interval: str is_observation: bool is_depth: bool - + i_min: int + i_max: int + j_min: int + j_max: int + k_min: int + k_max: int + z_min: float + z_max: float class SeismicFencePolyline(BaseModel): """ @@ -60,6 +68,38 @@ class SeismicFenceData(BaseModel): max_fence_depth: float +class SeismicInlineData(BaseModel): + slice_traces_b64arr: B64FloatArray + start_utm_x: float + start_utm_y: float + end_utm_x: float + end_utm_y: float + crossline_min: int + crossline_max: int + crossline_no_samples: int + z_min: float + z_max: float + z_samples: int + z_unit: str + value_min: float + value_max: float + +class SeismicCrosslineData(BaseModel): + slice_traces_b64arr: B64FloatArray + start_utm_x: float + start_utm_y: float + end_utm_x: float + end_utm_y: float + inline_min: int + inline_max: int + inline_no_samples: int + z_min: float + z_max: float + z_samples: int + z_unit: str + value_min: float + value_max: float + class SurfaceMeshAndProperty(BaseModel): x_ori: float y_ori: float diff --git a/backend_py/primary/primary/services/sumo_access/seismic_access.py b/backend_py/primary/primary/services/sumo_access/seismic_access.py index 699e2b208..eba47c63a 100644 --- a/backend_py/primary/primary/services/sumo_access/seismic_access.py +++ b/backend_py/primary/primary/services/sumo_access/seismic_access.py @@ -42,12 +42,19 @@ async def get_seismic_cube_meta_list_async(self) -> List[SeismicCubeMeta]: else: iso_string_or_time_interval = f"{t_start}/{t_end}" - seismic_meta = SeismicCubeMeta( seismic_attribute=cube["data"].get("tagname"), iso_date_or_interval=iso_string_or_time_interval, is_observation=cube["data"]["is_observation"], is_depth=cube["data"].get("vertical_domain", "depth") == "depth", + i_min=0, + i_max=cube["data"]["spec"]["ncol"]-1, + j_min=0, + j_max=cube["data"]["spec"]["nrow"]-1, + k_min=0, + k_max=cube["data"]["spec"]["nlay"]-1, + z_min=cube["data"]["bbox"]["zmin"], + z_max=cube["data"]["bbox"]["zmax"], ) seismic_cube_meta_list.append(seismic_meta) return seismic_cube_meta_list diff --git a/backend_py/primary/primary/services/sumo_access/seismic_types.py b/backend_py/primary/primary/services/sumo_access/seismic_types.py index 4a306f0d3..f22650885 100644 --- a/backend_py/primary/primary/services/sumo_access/seismic_types.py +++ b/backend_py/primary/primary/services/sumo_access/seismic_types.py @@ -6,6 +6,14 @@ class SeismicCubeMeta(BaseModel): iso_date_or_interval: str is_observation: bool is_depth: bool + i_min: int + i_max: int + j_min: int + j_max: int + k_min: int + k_max: int + z_min: float + z_max: float class VdsHandle(BaseModel): diff --git a/backend_py/primary/primary/services/vds_access/request_types.py b/backend_py/primary/primary/services/vds_access/request_types.py index 5e852d621..b9e9384c9 100644 --- a/backend_py/primary/primary/services/vds_access/request_types.py +++ b/backend_py/primary/primary/services/vds_access/request_types.py @@ -28,7 +28,11 @@ class VdsInterpolation(StrEnum): ANGULAR = "angular" TRIANGULAR = "triangular" - +class VdsDirection(StrEnum): + INLINE = "inline" + CROSSLINE = "crossline" + SAMPLE = "sample" + class VdsCoordinateSystem(StrEnum): """ Coordinate system options for vds fence @@ -119,3 +123,22 @@ def request_parameters(self) -> dict: "interpolation": self.interpolation.value, "fillValue": self.fill_value, } + +@dataclass +class VdsSliceRequest(VdsRequestedResource): + """ + Definition of a slice request struct for vds-slice + See: https://github.com/equinor/oneseismic-api/blob/ab6f39789bf3d3b59a8df14f1c4682d340dc0bf3/api/request.go#L143-L185 + """ + + direction: str + line_no: int + + + def request_parameters(self) -> dict: + return { + "vds": self.vds, + "sas": self.sas, + "direction": self.direction.lower(), + "lineno": self.line_no, + } \ No newline at end of file diff --git a/backend_py/primary/primary/services/vds_access/response_types.py b/backend_py/primary/primary/services/vds_access/response_types.py index d550b8371..7f9135bf1 100644 --- a/backend_py/primary/primary/services/vds_access/response_types.py +++ b/backend_py/primary/primary/services/vds_access/response_types.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from typing import List - +from enum import StrEnum from pydantic import BaseModel ###################################################################################################### @@ -14,7 +14,10 @@ # https://github.com/equinor/vds-slice/blob/ab6f39789bf3d3b59a8df14f1c4682d340dc0bf3/internal/core/core.go # ###################################################################################################### - +class VdsDirection(StrEnum): + INLINE = "Inline" + CROSSLINE = "Crossline" + SAMPLE = "Sample" @dataclass class VdsArray: @@ -32,6 +35,21 @@ class VdsArray: format: str shape: List[int] +class VdsAxis(BaseModel): + """ + Definition of an axis from vds-slice + + Neglected: + - stepsize: float + + See: https://github.com/equinor/vds-slice/blob/ab6f39789bf3d3b59a8df14f1c4682d340dc0bf3/internal/core/core.go#L37-L55 + """ + + annotation: VdsDirection + max: int + min: int + samples: int + unit: str @dataclass class VdsFenceMetadata(VdsArray): @@ -41,22 +59,18 @@ class VdsFenceMetadata(VdsArray): See: https://github.com/equinor/vds-slice/blob/ab6f39789bf3d3b59a8df14f1c4682d340dc0bf3/internal/core/core.go#L160-L162 """ - -class VdsAxis(BaseModel): +@dataclass +class VdsSliceMetadata(VdsArray): """ - Definition of an axis from vds-slice - - Neglected: - - stepsize: float + Definition of a fence metadata response from vds-slice - See: https://github.com/equinor/vds-slice/blob/ab6f39789bf3d3b59a8df14f1c4682d340dc0bf3/internal/core/core.go#L37-L55 + See: https://github.com/equinor/vds-slice/blob/ab6f39789bf3d3b59a8df14f1c4682d340dc0bf3/internal/core/core.go#L160-L162 """ + x: VdsAxis + y: VdsAxis + geospatial: List[List[float]] + # shape: List[int] - annotation: str - max: float - min: float - samples: int - unit: str class VdsBoundingBox(BaseModel): diff --git a/backend_py/primary/primary/services/vds_access/vds_access.py b/backend_py/primary/primary/services/vds_access/vds_access.py index acc99de4c..95f29b6a9 100644 --- a/backend_py/primary/primary/services/vds_access/vds_access.py +++ b/backend_py/primary/primary/services/vds_access/vds_access.py @@ -9,7 +9,7 @@ from primary import config -from .response_types import VdsMetadata, VdsFenceMetadata +from .response_types import VdsMetadata, VdsFenceMetadata, VdsSliceMetadata from .request_types import ( VdsCoordinates, VdsCoordinateSystem, @@ -17,6 +17,8 @@ VdsFenceRequest, VdsRequestedResource, VdsMetadataRequest, + VdsSliceRequest, + VdsDirection ) @@ -79,6 +81,83 @@ async def get_metadata_async(self) -> VdsMetadata: metadata = response.json() return VdsMetadata(**metadata) + + async def get_inline_slice(self,line_no:int ) -> Tuple[NDArray[np.float32], VdsSliceMetadata]: + endpoint = "slice" + hard_coded_fill_value = -999.25 + slice_request = VdsSliceRequest( + vds=self.vds_url, + sas=self.sas, + direction=VdsDirection.INLINE, + line_no=line_no, + ) + response = await self._query_async(endpoint, slice_request) + decoder = MultipartDecoder(content=response.content, content_type=response.headers["Content-Type"]) + parts = decoder.parts + # Validate parts from decoded response + if len(parts) != 2 or not parts[0].content or not parts[1].content: + raise ValueError(f"Expected two parts, got {len(parts)}") + + # Expect each part in parts tuple to be BodyPart + if not isinstance(parts[0], BodyPart) or not isinstance(parts[1], BodyPart): + raise ValueError(f"Expected parts to be BodyPart, got {type(parts[0])}, {type(parts[1])}") + + metadata = VdsSliceMetadata(**json.loads(parts[0].content)) + byte_array = parts[1].content + + if metadata.format != " Tuple[NDArray[np.float32], VdsSliceMetadata]: + endpoint = "slice" + hard_coded_fill_value = -999.25 + slice_request = VdsSliceRequest( + vds=self.vds_url, + sas=self.sas, + direction=VdsDirection.CROSSLINE, + line_no=line_no, + ) + response = await self._query_async(endpoint, slice_request) + decoder = MultipartDecoder(content=response.content, content_type=response.headers["Content-Type"]) + parts = decoder.parts + # Validate parts from decoded response + if len(parts) != 2 or not parts[0].content or not parts[1].content: + raise ValueError(f"Expected two parts, got {len(parts)}") + + # Expect each part in parts tuple to be BodyPart + if not isinstance(parts[0], BodyPart) or not isinstance(parts[1], BodyPart): + raise ValueError(f"Expected parts to be BodyPart, got {type(parts[0])}, {type(parts[1])}") + + metadata = VdsSliceMetadata(**json.loads(parts[0].content)) + byte_array = parts[1].content + + if metadata.format != " Tuple[NDArray[np.float32], int, int]: @@ -184,5 +263,5 @@ async def get_flattened_fence_traces_array_and_metadata_async( # Convert every value of `hard_coded_fill_value` to np.nan flattened_fence_traces_float32_array[flattened_fence_traces_float32_array == hard_coded_fill_value] = np.nan - + print("flattened fence data", flattened_fence_traces_float32_array, len(flattened_fence_traces_float32_array)) return (flattened_fence_traces_float32_array, num_traces, num_samples_per_trace) diff --git a/frontend/src/api/autogen/@tanstack/react-query.gen.ts b/frontend/src/api/autogen/@tanstack/react-query.gen.ts index 4a855309e..208ace2d7 100644 --- a/frontend/src/api/autogen/@tanstack/react-query.gen.ts +++ b/frontend/src/api/autogen/@tanstack/react-query.gen.ts @@ -10,6 +10,7 @@ import { getAlive, getAliveProtected, getCases, + getCrosslineSlice, getDeltaEnsembleRealizationsVectorData, getDeltaEnsembleStatisticalVectorData, getDeltaEnsembleVectorList, @@ -22,6 +23,7 @@ import { getGridParameter, getGridSurface, getHistoricalVectorData, + getInlineSlice, getIsGridGeometryShared, getIsSensitivityRun, getLogCurveData, @@ -77,6 +79,7 @@ import type { GetAliveData_api, GetAliveProtectedData_api, GetCasesData_api, + GetCrosslineSliceData_api, GetDeltaEnsembleRealizationsVectorDataData_api, GetDeltaEnsembleStatisticalVectorDataData_api, GetDeltaEnsembleVectorListData_api, @@ -89,6 +92,7 @@ import type { GetGridParameterData_api, GetGridSurfaceData_api, GetHistoricalVectorDataData_api, + GetInlineSliceData_api, GetIsGridGeometrySharedData_api, GetIsSensitivityRunData_api, GetLogCurveDataData_api, @@ -1230,6 +1234,44 @@ export const getSeismicCubeMetaListOptions = (options: Options) => [ + createQueryKey("getInlineSlice", options), +]; + +export const getInlineSliceOptions = (options: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getInlineSlice({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }); + return data; + }, + queryKey: getInlineSliceQueryKey(options), + }); +}; + +export const getCrosslineSliceQueryKey = (options: Options) => [ + createQueryKey("getCrosslineSlice", options), +]; + +export const getCrosslineSliceOptions = (options: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getCrosslineSlice({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }); + return data; + }, + queryKey: getCrosslineSliceQueryKey(options), + }); +}; + export const postGetSeismicFenceQueryKey = (options: Options) => [ createQueryKey("postGetSeismicFence", options), ]; diff --git a/frontend/src/api/autogen/sdk.gen.ts b/frontend/src/api/autogen/sdk.gen.ts index 7caa1b280..2720ea98f 100644 --- a/frontend/src/api/autogen/sdk.gen.ts +++ b/frontend/src/api/autogen/sdk.gen.ts @@ -10,6 +10,9 @@ import type { GetCasesData_api, GetCasesError_api, GetCasesResponse_api, + GetCrosslineSliceData_api, + GetCrosslineSliceError_api, + GetCrosslineSliceResponse_api, GetDeltaEnsembleRealizationsVectorDataData_api, GetDeltaEnsembleRealizationsVectorDataError_api, GetDeltaEnsembleRealizationsVectorDataResponse_api, @@ -45,6 +48,9 @@ import type { GetHistoricalVectorDataData_api, GetHistoricalVectorDataError_api, GetHistoricalVectorDataResponse_api, + GetInlineSliceData_api, + GetInlineSliceError_api, + GetInlineSliceResponse_api, GetIsGridGeometrySharedData_api, GetIsGridGeometrySharedError_api, GetIsGridGeometrySharedResponse_api, @@ -990,6 +996,38 @@ export const getSeismicCubeMetaList = ( }); }; +/** + * Get Inline Slice + * Get a seismic slice from a seismic cube. + * + * Returns: + * A SeismicFenceData object with fence traces in encoded 1D array, metadata for trace array decoding and fence min/max depth. + */ +export const getInlineSlice = ( + options: Options +) => { + return (options?.client ?? client).get({ + ...options, + url: "/seismic/get_inline_slice/", + }); +}; + +/** + * Get Crossline Slice + * Get a seismic slice from a seismic cube. + * + * Returns: + * A SeismicFenceData object with fence traces in encoded 1D array, metadata for trace array decoding and fence min/max depth. + */ +export const getCrosslineSlice = ( + options: Options +) => { + return (options?.client ?? client).get({ + ...options, + url: "/seismic/get_crossline_slice/", + }); +}; + /** * Post Get Seismic Fence * Get a fence of seismic data from a polyline defined by a set of (x, y) coordinates in domain coordinate system. diff --git a/frontend/src/api/autogen/types.gen.ts b/frontend/src/api/autogen/types.gen.ts index c9e2f6656..4a021b9dc 100644 --- a/frontend/src/api/autogen/types.gen.ts +++ b/frontend/src/api/autogen/types.gen.ts @@ -544,11 +544,36 @@ export type RftWellInfo_api = { timestamps_utc_ms: Array; }; +export type SeismicCrosslineData_api = { + slice_traces_b64arr: B64FloatArray_api; + start_utm_x: number; + start_utm_y: number; + end_utm_x: number; + end_utm_y: number; + inline_min: number; + inline_max: number; + inline_no_samples: number; + z_min: number; + z_max: number; + z_samples: number; + z_unit: string; + value_min: number; + value_max: number; +}; + export type SeismicCubeMeta_api = { seismic_attribute: string; iso_date_or_interval: string; is_observation: boolean; is_depth: boolean; + i_min: number; + i_max: number; + j_min: number; + j_max: number; + k_min: number; + k_max: number; + z_min: number; + z_max: number; }; /** @@ -600,6 +625,23 @@ export type SeismicFencePolyline_api = { y_points: Array; }; +export type SeismicInlineData_api = { + slice_traces_b64arr: B64FloatArray_api; + start_utm_x: number; + start_utm_y: number; + end_utm_x: number; + end_utm_y: number; + crossline_min: number; + crossline_max: number; + crossline_no_samples: number; + z_min: number; + z_max: number; + z_samples: number; + z_unit: string; + value_min: number; + value_max: number; +}; + export enum SensitivityType_api { MONTECARLO = "montecarlo", SCENARIO = "scenario", @@ -3072,6 +3114,114 @@ export type GetSeismicCubeMetaListResponses_api = { export type GetSeismicCubeMetaListResponse_api = GetSeismicCubeMetaListResponses_api[keyof GetSeismicCubeMetaListResponses_api]; +export type GetInlineSliceData_api = { + body?: never; + path?: never; + query: { + /** + * Sumo case uuid + */ + case_uuid: string; + /** + * Ensemble name + */ + ensemble_name: string; + /** + * Realization number + */ + realization_num: number; + /** + * Seismic cube attribute + */ + seismic_attribute: string; + /** + * Timestamp or timestep + */ + time_or_interval_str: string; + /** + * Observed or simulated + */ + observed: boolean; + /** + * Inline number + */ + inline_no: number; + }; + url: "/seismic/get_inline_slice/"; +}; + +export type GetInlineSliceErrors_api = { + /** + * Validation Error + */ + 422: HttpValidationError_api; +}; + +export type GetInlineSliceError_api = GetInlineSliceErrors_api[keyof GetInlineSliceErrors_api]; + +export type GetInlineSliceResponses_api = { + /** + * Successful Response + */ + 200: SeismicInlineData_api; +}; + +export type GetInlineSliceResponse_api = GetInlineSliceResponses_api[keyof GetInlineSliceResponses_api]; + +export type GetCrosslineSliceData_api = { + body?: never; + path?: never; + query: { + /** + * Sumo case uuid + */ + case_uuid: string; + /** + * Ensemble name + */ + ensemble_name: string; + /** + * Realization number + */ + realization_num: number; + /** + * Seismic cube attribute + */ + seismic_attribute: string; + /** + * Timestamp or timestep + */ + time_or_interval_str: string; + /** + * Observed or simulated + */ + observed: boolean; + /** + * Crossline number + */ + crossline_no: number; + }; + url: "/seismic/get_crossline_slice/"; +}; + +export type GetCrosslineSliceErrors_api = { + /** + * Validation Error + */ + 422: HttpValidationError_api; +}; + +export type GetCrosslineSliceError_api = GetCrosslineSliceErrors_api[keyof GetCrosslineSliceErrors_api]; + +export type GetCrosslineSliceResponses_api = { + /** + * Successful Response + */ + 200: SeismicCrosslineData_api; +}; + +export type GetCrosslineSliceResponse_api = GetCrosslineSliceResponses_api[keyof GetCrosslineSliceResponses_api]; + export type PostGetSeismicFenceData_api = { body: BodyPostGetSeismicFence_api; path?: never; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts new file mode 100644 index 000000000..86530006e --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -0,0 +1,121 @@ +import { getCrosslineSliceOptions } from "@api"; +import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; +import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; +import { QueryClient } from "@tanstack/react-query"; + +import { isEqual } from "lodash"; + +import { RealizationSeismicCrosslineSettingsContext } from "./RealizationSeismicCrosslineSettingsContext"; +import { RealizationSeismicCrosslineSettings } from "./types"; + +import { SeismicCrosslineData_trans, transformSeismicCrossline } from "../../../settings/queries/queryDataTransforms"; + +export class RealizationSeismicCrosslineLayer + implements Layer +{ + private _layerDelegate: LayerDelegate; + private _itemDelegate: ItemDelegate; + + constructor(layerManager: LayerManager) { + this._itemDelegate = new ItemDelegate("Realization Seismic Crossline", layerManager); + this._layerDelegate = new LayerDelegate( + this, + layerManager, + new RealizationSeismicCrosslineSettingsContext(layerManager), + LayerColoringType.COLORSCALE + ); + } + + getSettingsContext() { + return this._layerDelegate.getSettingsContext(); + } + + getItemDelegate(): ItemDelegate { + return this._itemDelegate; + } + + getLayerDelegate(): LayerDelegate { + return this._layerDelegate; + } + + doSettingsChangesRequireDataRefetch( + prevSettings: RealizationSeismicCrosslineSettings, + newSettings: RealizationSeismicCrosslineSettings + ): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeBoundingBox(): BoundingBox | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return { + x: [data.start_utm_x, data.end_utm_x], + y: [data.start_utm_y, data.end_utm_y], + z: [data.z_min, data.z_max], + }; + } + + makeValueRange(): [number, number] | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return [data.value_min, data.value_max]; + } + + fetchData(queryClient: QueryClient): Promise { + const settings = this.getSettingsContext().getDelegate().getSettings(); + const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); + const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); + const seismicAttribute = settings[SettingType.SEISMIC_ATTRIBUTE].getDelegate().getValue(); + + let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + const seismicCrosslineNumber = settings[SettingType.SEISMIC_CROSSLINE].getDelegate().getValue(); + + const queryKey = [ + "realizationSeismicCrosslineSlice", + ensembleIdent, + seismicAttribute, + timeOrInterval, + realizationNum, + seismicCrosslineNumber, + ]; + this._layerDelegate.registerQueryKey(queryKey); + + const seismicSlicePromise = queryClient + .fetchQuery({ + ...getCrosslineSliceOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + realization_num: realizationNum ?? 0, + seismic_attribute: seismicAttribute ?? "", + time_or_interval_str: timeOrInterval ?? "", + observed: false, + crossline_no: seismicCrosslineNumber ?? 0, + }, + }), + }) + .then((data) => transformSeismicCrossline(data)); + + return seismicSlicePromise; + } + + serializeState(): SerializedLayer { + return this._layerDelegate.serializeState(); + } + + deserializeState(serializedState: SerializedLayer): void { + this._layerDelegate.deserializeState(serializedState); + } +} + +LayerRegistry.registerLayer(RealizationSeismicCrosslineLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts new file mode 100644 index 000000000..271061c0d --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts @@ -0,0 +1,156 @@ +import { getGridModelsInfoOptions, getSeismicCubeMetaListOptions } from "@api"; +import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; +import { GridAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting"; +import { GridLayerIRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting"; +import { GridLayerJRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting"; +import { GridLayerKRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting"; +import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; +import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; +import { SeismicAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting"; +import { SeismicCrosslineSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting"; +import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; +import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +import { RealizationSeismicCrosslineSettings } from "./types"; + +export class RealizationSeismicCrosslineSettingsContext + implements SettingsContext +{ + private _contextDelegate: SettingsContextDelegate; + + constructor(layerManager: LayerManager) { + this._contextDelegate = new SettingsContextDelegate< + RealizationSeismicCrosslineSettings, + keyof RealizationSeismicCrosslineSettings + >(this, layerManager, { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.REALIZATION]: new RealizationSetting(), + [SettingType.SEISMIC_ATTRIBUTE]: new SeismicAttributeSetting(), + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + [SettingType.SEISMIC_CROSSLINE]: new SeismicCrosslineSetting(), + }); + } + + areCurrentSettingsValid(settings: RealizationSeismicCrosslineSettings): boolean { + return ( + settings[SettingType.ENSEMBLE] !== null && + settings[SettingType.REALIZATION] !== null && + settings[SettingType.SEISMIC_ATTRIBUTE] !== null && + settings[SettingType.TIME_OR_INTERVAL] !== null + ); + } + + getDelegate(): SettingsContextDelegate { + return this._contextDelegate; + } + + getSettings() { + return this._contextDelegate.getSettings(); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + queryClient, + }: DefineDependenciesArgs) { + availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); + + if (!ensembleIdent) { + return []; + } + + const realizations = realizationFilterFunc(ensembleIdent); + + return [...realizations]; + }); + const realizationSeismicCrosslineDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realization = getLocalSetting(SettingType.REALIZATION); + + if (!ensembleIdent || realization === null) { + return null; + } + + return await queryClient.fetchQuery({ + ...getSeismicCubeMetaListOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + }, + signal: abortSignal, + }), + }); + }); + + availableSettingsUpdater(SettingType.SEISMIC_ATTRIBUTE, ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!data) { + return []; + } + + const availableSeismicAttributes = [ + ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismic_attribute))), + ]; + + return availableSeismicAttributes; + }); + + availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!seismicAttribute || !data) { + return []; + } + + const availableTimeOrIntervals = [ + ...Array.from( + new Set( + data + .filter((surface) => surface.seismic_attribute === seismicAttribute) + .map((el) => el.iso_date_or_interval) + ) + ), + ]; + + return availableTimeOrIntervals; + }); + availableSettingsUpdater(SettingType.SEISMIC_CROSSLINE, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + const timeOrInterval = getLocalSetting(SettingType.TIME_OR_INTERVAL); + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!seismicAttribute || !timeOrInterval || !data) { + return []; + } + const seismicInfo = data.filter( + (seismicInfos) => + seismicInfos.seismic_attribute === seismicAttribute && + seismicInfos.iso_date_or_interval === timeOrInterval + )[0]; + const jMin = seismicInfo.j_min; + const jMax = seismicInfo.j_max; + + return [jMin, jMax]; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/index.ts new file mode 100644 index 000000000..67f5bab9f --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/index.ts @@ -0,0 +1,2 @@ +export { RealizationSeismicCrosslineLayer } from "./RealizationSeismicCrosslineLayer"; +export { RealizationSeismicCrosslineSettingsContext } from "./RealizationSeismicCrosslineSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts new file mode 100644 index 000000000..774983ff4 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts @@ -0,0 +1,10 @@ +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +export type RealizationSeismicCrosslineSettings = { + [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; + [SettingType.REALIZATION]: number | null; + [SettingType.SEISMIC_ATTRIBUTE]: string | null; + [SettingType.TIME_OR_INTERVAL]: string | null; + [SettingType.SEISMIC_CROSSLINE]: number | null; +}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts new file mode 100644 index 000000000..c2602fb7f --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts @@ -0,0 +1,119 @@ +import { getInlineSliceOptions } from "@api"; +import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; +import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; +import { QueryClient } from "@tanstack/react-query"; + +import { isEqual } from "lodash"; + +import { RealizationSeismicInlineSettingsContext } from "./RealizationSeismicInlineSettingsContext"; +import { RealizationSeismicInlineSettings } from "./types"; + +import { SeismicInlineData_trans, transformSeismicInline } from "../../../settings/queries/queryDataTransforms"; + +export class RealizationSeismicInlineLayer implements Layer { + private _layerDelegate: LayerDelegate; + private _itemDelegate: ItemDelegate; + + constructor(layerManager: LayerManager) { + this._itemDelegate = new ItemDelegate("Realization Seismic inline", layerManager); + this._layerDelegate = new LayerDelegate( + this, + layerManager, + new RealizationSeismicInlineSettingsContext(layerManager), + LayerColoringType.COLORSCALE + ); + } + + getSettingsContext() { + return this._layerDelegate.getSettingsContext(); + } + + getItemDelegate(): ItemDelegate { + return this._itemDelegate; + } + + getLayerDelegate(): LayerDelegate { + return this._layerDelegate; + } + + doSettingsChangesRequireDataRefetch( + prevSettings: RealizationSeismicInlineSettings, + newSettings: RealizationSeismicInlineSettings + ): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeBoundingBox(): BoundingBox | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return { + x: [data.start_utm_x, data.end_utm_x], + y: [data.start_utm_y, data.end_utm_y], + z: [data.z_min, data.z_max], + }; + } + + makeValueRange(): [number, number] | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return [data.value_min, data.value_max]; + } + + fetchData(queryClient: QueryClient): Promise { + const settings = this.getSettingsContext().getDelegate().getSettings(); + const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); + const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); + const seismicAttribute = settings[SettingType.SEISMIC_ATTRIBUTE].getDelegate().getValue(); + + let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + const seismicInlineNumber = settings[SettingType.SEISMIC_INLINE].getDelegate().getValue(); + + const queryKey = [ + "realizationSeismicInlineSlice", + ensembleIdent, + seismicAttribute, + timeOrInterval, + realizationNum, + seismicInlineNumber, + ]; + this._layerDelegate.registerQueryKey(queryKey); + + const seismicSlicePromise = queryClient + .fetchQuery({ + ...getInlineSliceOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + realization_num: realizationNum ?? 0, + seismic_attribute: seismicAttribute ?? "", + time_or_interval_str: timeOrInterval ?? "", + observed: false, + inline_no: seismicInlineNumber ?? 0, + }, + }), + }) + .then((data) => transformSeismicInline(data)); + + return seismicSlicePromise; + } + + serializeState(): SerializedLayer { + return this._layerDelegate.serializeState(); + } + + deserializeState(serializedState: SerializedLayer): void { + this._layerDelegate.deserializeState(serializedState); + } +} + +LayerRegistry.registerLayer(RealizationSeismicInlineLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts new file mode 100644 index 000000000..3967d2af5 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts @@ -0,0 +1,155 @@ +import { getGridModelsInfoOptions, getSeismicCubeMetaListOptions } from "@api"; +import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; +import { GridAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting"; +import { GridLayerIRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting"; +import { GridLayerJRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting"; +import { GridLayerKRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting"; +import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; +import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; +import { SeismicAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting"; +import { SeismicInlineSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting"; +import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; +import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +import { RealizationSeismicInlineSettings } from "./types"; + +export class RealizationSeismicInlineSettingsContext implements SettingsContext { + private _contextDelegate: SettingsContextDelegate; + + constructor(layerManager: LayerManager) { + this._contextDelegate = new SettingsContextDelegate< + RealizationSeismicInlineSettings, + keyof RealizationSeismicInlineSettings + >(this, layerManager, { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.REALIZATION]: new RealizationSetting(), + [SettingType.SEISMIC_ATTRIBUTE]: new SeismicAttributeSetting(), + + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + [SettingType.SEISMIC_INLINE]: new SeismicInlineSetting(), + }); + } + + areCurrentSettingsValid(settings: RealizationSeismicInlineSettings): boolean { + return ( + settings[SettingType.ENSEMBLE] !== null && + settings[SettingType.REALIZATION] !== null && + settings[SettingType.SEISMIC_ATTRIBUTE] !== null && + settings[SettingType.TIME_OR_INTERVAL] !== null + ); + } + + getDelegate(): SettingsContextDelegate { + return this._contextDelegate; + } + + getSettings() { + return this._contextDelegate.getSettings(); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + queryClient, + }: DefineDependenciesArgs) { + availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); + + if (!ensembleIdent) { + return []; + } + + const realizations = realizationFilterFunc(ensembleIdent); + + return [...realizations]; + }); + const realizationSeismicInlineDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realization = getLocalSetting(SettingType.REALIZATION); + + if (!ensembleIdent || realization === null) { + return null; + } + + return await queryClient.fetchQuery({ + ...getSeismicCubeMetaListOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + }, + signal: abortSignal, + }), + }); + }); + + availableSettingsUpdater(SettingType.SEISMIC_ATTRIBUTE, ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSeismicInlineDataDep); + + if (!data) { + return []; + } + + const availableSeismicAttributes = [ + ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismic_attribute))), + ]; + + return availableSeismicAttributes; + }); + + availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + + const data = getHelperDependency(realizationSeismicInlineDataDep); + + if (!seismicAttribute || !data) { + return []; + } + + const availableTimeOrIntervals = [ + ...Array.from( + new Set( + data + .filter((surface) => surface.seismic_attribute === seismicAttribute) + .map((el) => el.iso_date_or_interval) + ) + ), + ]; + + return availableTimeOrIntervals; + }); + availableSettingsUpdater(SettingType.SEISMIC_INLINE, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + const timeOrInterval = getLocalSetting(SettingType.TIME_OR_INTERVAL); + const data = getHelperDependency(realizationSeismicInlineDataDep); + + if (!seismicAttribute || !timeOrInterval || !data) { + return []; + } + const seismicInfo = data.filter( + (seismicInfos) => + seismicInfos.seismic_attribute === seismicAttribute && + seismicInfos.iso_date_or_interval === timeOrInterval + )[0]; + const iMin = seismicInfo.i_min; + const iMax = seismicInfo.i_max; + + return [iMin, iMax]; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/index.ts new file mode 100644 index 000000000..8ee59b2a9 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/index.ts @@ -0,0 +1,2 @@ +export { RealizationSeismicInlineLayer } from "./RealizationSeismicInlineLayer"; +export { RealizationSeismicInlineSettingsContext } from "./RealizationSeismicInlineSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts new file mode 100644 index 000000000..e09557287 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts @@ -0,0 +1,10 @@ +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +export type RealizationSeismicInlineSettings = { + [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; + [SettingType.REALIZATION]: number | null; + [SettingType.SEISMIC_ATTRIBUTE]: string | null; + [SettingType.TIME_OR_INTERVAL]: string | null; + [SettingType.SEISMIC_INLINE]: number | null; +}; diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index faa7022d8..7b44f774e 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -11,6 +11,7 @@ import { MenuItem } from "@lib/components/MenuItem"; import { ObservedSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer"; import { StatisticalSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; import { PreferredViewLayout } from "@modules/2DViewer/types"; +import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; import { EnsembleSetting } from "@modules//_shared/LayerFramework/settings/implementations/EnsembleSetting"; import { LayersActionGroup } from "@modules/_shared/LayerFramework/LayersActions"; import { GroupDelegate, GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; @@ -39,6 +40,7 @@ import { import { useAtom } from "jotai"; import { RealizationGridLayer } from "../../LayerFramework/customLayerImplementations/RealizationGridLayer"; +import { RealizationSeismicInlineLayer } from "../../LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer"; import { RealizationSurfaceLayer } from "../../LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { preferredViewLayoutAtom } from "../atoms/baseAtoms"; @@ -83,6 +85,12 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper case "realization-surface": groupDelegate.insertChild(new RealizationSurfaceLayer(props.layerManager), numSharedSettings); return; + case "realization-seismic-inline": + groupDelegate.insertChild(new RealizationSeismicInlineLayer(props.layerManager), numSharedSettings); + return; + case "realization-seismic-crossline": + groupDelegate.insertChild(new RealizationSeismicCrosslineLayer(props.layerManager), numSharedSettings); + return; case "drilled-wellbore-trajectories": groupDelegate.insertChild(new DrilledWellTrajectoriesLayer(props.layerManager), numSharedSettings); return; @@ -240,6 +248,16 @@ const LAYER_ACTIONS: LayersActionGroup[] = [ icon: , label: "Realization Surface", }, + { + identifier: "realization-seismic-inline", + icon: , + label: "Realization Seismic Inline", + }, + { + identifier: "realization-seismic-crossline", + icon: , + label: "Realization Seismic Crossline", + }, ], }, ], diff --git a/frontend/src/modules/3DViewerNew/settings/queries/queryDataTransforms.ts b/frontend/src/modules/3DViewerNew/settings/queries/queryDataTransforms.ts new file mode 100644 index 000000000..430fa4555 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/settings/queries/queryDataTransforms.ts @@ -0,0 +1,38 @@ +import { SeismicCrosslineData_api, SeismicInlineData_api } from "@api"; +import { b64DecodeFloatArrayToFloat32 } from "@modules/_shared/base64"; + +export type SeismicInlineData_trans = Omit & { + dataFloat32Arr: Float32Array; +}; + +export function transformSeismicInline(apiData: SeismicInlineData_api): SeismicInlineData_trans { + const startTS = performance.now(); + + const { slice_traces_b64arr, ...untransformedData } = apiData; + const dataFloat32Arr = b64DecodeFloatArrayToFloat32(slice_traces_b64arr); + + console.debug(`transformSeismicInline() took: ${(performance.now() - startTS).toFixed(1)}ms`); + + return { + ...untransformedData, + dataFloat32Arr: dataFloat32Arr, + }; +} + +export type SeismicCrosslineData_trans = Omit & { + dataFloat32Arr: Float32Array; +}; + +export function transformSeismicCrossline(apiData: SeismicCrosslineData_api): SeismicCrosslineData_trans { + const startTS = performance.now(); + + const { slice_traces_b64arr, ...untransformedData } = apiData; + const dataFloat32Arr = b64DecodeFloatArrayToFloat32(slice_traces_b64arr); + + console.debug(`transformSeismicCrossline() took: ${(performance.now() - startTS).toFixed(1)}ms`); + + return { + ...untransformedData, + dataFloat32Arr: dataFloat32Arr, + }; +} diff --git a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts index 3b5725cfe..d0374347f 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts @@ -3,16 +3,25 @@ import { Layer } from "@deck.gl/core"; import { defaultColorPalettes } from "@framework/utils/colorPalettes"; import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; import { GridMappedProperty_trans, GridSurface_trans } from "@modules/3DViewer/view/queries/queryDataTransforms"; +import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; +import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; +import { + SeismicCrosslineData_trans, + SeismicInlineData_trans, +} from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; import { Layer as LayerInterface } from "@modules/_shared/LayerFramework/interfaces"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; import { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataTransforms"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; +import { TGrid3DColoringMode } from "@webviz/subsurface-viewer"; import { Grid3DLayer, MapLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; import { Rgb, parse } from "culori"; import { Feature } from "geojson"; +import { createSeismicCrosslineLayerData, createSeismicInlineLayerData } from "./seismicSliceUtils"; + import { RealizationGridLayer } from "../../LayerFramework/customLayerImplementations/RealizationGridLayer"; import { RealizationSurfaceLayer } from "../../LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { AdvancedWellsLayer } from "../customDeckGlLayers/AdvancedWellsLayer"; @@ -50,7 +59,6 @@ export function makeDeckGlLayer(layer: LayerInterface, colorScale?: Co ); } if (layer instanceof RealizationSurfaceLayer) { - console.log(data); return createSurfaceMeshLayer( data, layer.getItemDelegate().getId(), @@ -58,8 +66,57 @@ export function makeDeckGlLayer(layer: LayerInterface, colorScale?: Co colorScale ); } + if (layer instanceof RealizationSeismicInlineLayer) { + const seismicData: SeismicInlineData_trans = data; + const seismicLayerData = createSeismicInlineLayerData(seismicData); + + const coloringMode: TGrid3DColoringMode = TGrid3DColoringMode.Property; + const grid3dLayer = new Grid3DLayer({ + id: layer.getItemDelegate().getId(), + name: layer.getItemDelegate().getName(), + pointsData: seismicLayerData.pointsFloat32Arr, + polysData: seismicLayerData.polysUint32Arr, + propertiesData: seismicLayerData.propertyFloat32Arr, + ZIncreasingDownwards: true, + gridLines: false, + colorMapRange: [seismicLayerData.minValue, seismicLayerData.maxValue], + colorMapName: "Physics", + colorMapClampColor: true, + coloringMode: coloringMode, + material: { ambient: 0, diffuse: 0.7, shininess: 1, specularColor: [25, 25, 25] }, + colorMapFunction: makeColorMapFunction(colorScale, seismicLayerData.minValue, seismicLayerData.maxValue), + + pickable: true, + }); + return grid3dLayer as unknown as WorkingGrid3dLayer; + } + if (layer instanceof RealizationSeismicCrosslineLayer) { + const seismicData: SeismicCrosslineData_trans = data; + const seismicLayerData = createSeismicCrosslineLayerData(seismicData); + + const coloringMode: TGrid3DColoringMode = TGrid3DColoringMode.Property; + const grid3dLayer = new Grid3DLayer({ + id: layer.getItemDelegate().getId(), + name: layer.getItemDelegate().getName(), + pointsData: seismicLayerData.pointsFloat32Arr, + polysData: seismicLayerData.polysUint32Arr, + propertiesData: seismicLayerData.propertyFloat32Arr, + ZIncreasingDownwards: true, + gridLines: false, + colorMapRange: [seismicLayerData.minValue, seismicLayerData.maxValue], + colorMapName: "Physics", + colorMapClampColor: true, + coloringMode: coloringMode, + material: { ambient: 0, diffuse: 0.7, shininess: 1, specularColor: [25, 25, 25] }, + colorMapFunction: makeColorMapFunction(colorScale, seismicLayerData.minValue, seismicLayerData.maxValue), + + pickable: true, + }); + return grid3dLayer as unknown as WorkingGrid3dLayer; + } return null; } + export function createSurfaceMeshLayer( layerData: SurfaceDataFloat_trans, id: string, @@ -86,9 +143,9 @@ export function createSurfaceMeshLayer( }, valueRange: [layerData.value_min, layerData.value_max], colorMapRange: [layerData.value_min, layerData.value_max], - isContoursDepth: true, - contours: [0, 10], - gridLines: true, + // isContoursDepth: true, + // contours: [0, 10], + gridLines: false, material: useMaterial, smoothShading: useSmoothShading, colorMapName: "Physics", diff --git a/frontend/src/modules/3DViewerNew/view/utils/seismicSliceUtils.ts b/frontend/src/modules/3DViewerNew/view/utils/seismicSliceUtils.ts new file mode 100644 index 000000000..78e3763cc --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/utils/seismicSliceUtils.ts @@ -0,0 +1,160 @@ +import { + SeismicCrosslineData_trans, + SeismicInlineData_trans, +} from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; + +export type SeismicInlineLayerData = { + pointsFloat32Arr: Float32Array; + polysUint32Arr: Uint32Array; + propertyFloat32Arr: Float32Array; + minValue: number; + maxValue: number; +}; +export type SeismicCrosslineLayerData = { + pointsFloat32Arr: Float32Array; + polysUint32Arr: Uint32Array; + propertyFloat32Arr: Float32Array; + minValue: number; + maxValue: number; +}; +type Point = { x: number; y: number }; + +function interpolatePoints(start: Point, end: Point, numSamples: number): Point[] { + const result: Point[] = []; + + const intervalX = (end.x - start.x) / (numSamples - 1); + + for (let i = 0; i < numSamples; i++) { + const x = start.x + i * intervalX; + const y = start.y + ((x - start.x) / (end.x - start.x)) * (end.y - start.y); + result.push({ x, y }); + } + + return result; +} +export function createSeismicInlineLayerData(seismicInlineData: SeismicInlineData_trans): SeismicInlineLayerData { + const origPoint: Point = { x: seismicInlineData.start_utm_x, y: seismicInlineData.start_utm_y }; + const endPoint: Point = { x: seismicInlineData.end_utm_x, y: seismicInlineData.end_utm_y }; + const numSamples = seismicInlineData.crossline_no_samples; + const interpolatedPointsXY = interpolatePoints(origPoint, endPoint, numSamples); + + const minDepth = seismicInlineData.z_min; + const maxDepth = seismicInlineData.z_max; + const depthSamples = seismicInlineData.z_samples; + + const cellCountU = interpolatedPointsXY.length - 1; + const cellCountV = depthSamples - 1; + + const depthIncrement = (maxDepth - minDepth) / depthSamples; + const pointsFloat32Arr = new Float32Array((cellCountU + 1) * (cellCountV + 1) * 3); + const polysUint32Arr = new Uint32Array(cellCountU * cellCountV * 5); // 5 = 4 vertices + polygon count + + let pointIndex = 0; + let polygonIndex = 0; + + for (let j = 0; j <= cellCountV; j++) { + for (let i = 0; i <= cellCountU; i++) { + pointsFloat32Arr[pointIndex++] = interpolatedPointsXY[i].x; + pointsFloat32Arr[pointIndex++] = interpolatedPointsXY[i].y; + pointsFloat32Arr[pointIndex++] = minDepth + j * depthIncrement; + + if (i < cellCountU && j < cellCountV) { + polysUint32Arr[polygonIndex++] = 4; + polysUint32Arr[polygonIndex++] = i + j * (cellCountU + 1); + polysUint32Arr[polygonIndex++] = i + 1 + j * (cellCountU + 1); + polysUint32Arr[polygonIndex++] = i + 1 + (j + 1) * (cellCountU + 1); + polysUint32Arr[polygonIndex++] = i + (j + 1) * (cellCountU + 1); + } + } + } + const propertyArr = seismicInlineData.dataFloat32Arr; + const transposedPropertyArr = []; + for (let j = 0; j < depthSamples; j++) { + for (let i = 0; i < cellCountU; i++) { + transposedPropertyArr.push(propertyArr[i * depthSamples + j]); + } + } + const chunkSize = 10000; + let minValue = Infinity; + let maxValue = -Infinity; + + for (let i = 0; i < transposedPropertyArr.length; i += chunkSize) { + const chunk = transposedPropertyArr.slice(i, i + chunkSize); + const chunkMin = Math.min(...chunk); + const chunkMax = Math.max(...chunk); + + if (chunkMin < minValue) minValue = chunkMin; + if (chunkMax > maxValue) maxValue = chunkMax; + } + return { + pointsFloat32Arr, + polysUint32Arr, + propertyFloat32Arr: new Float32Array(transposedPropertyArr), + minValue, + maxValue, + }; +} +export function createSeismicCrosslineLayerData( + seismicCrosslineData: SeismicCrosslineData_trans +): SeismicCrosslineLayerData { + const origPoint: Point = { x: seismicCrosslineData.start_utm_x, y: seismicCrosslineData.start_utm_y }; + const endPoint: Point = { x: seismicCrosslineData.end_utm_x, y: seismicCrosslineData.end_utm_y }; + const numSamples = seismicCrosslineData.inline_no_samples; + const interpolatedPointsXY = interpolatePoints(origPoint, endPoint, numSamples); + + const minDepth = seismicCrosslineData.z_min; + const maxDepth = seismicCrosslineData.z_max; + const depthSamples = seismicCrosslineData.z_samples; + + const cellCountU = interpolatedPointsXY.length - 1; + const cellCountV = depthSamples - 1; + + const depthIncrement = (maxDepth - minDepth) / depthSamples; + const pointsFloat32Arr = new Float32Array((cellCountU + 1) * (cellCountV + 1) * 3); + const polysUint32Arr = new Uint32Array(cellCountU * cellCountV * 5); // 5 = 4 vertices + polygon count + + let pointIndex = 0; + let polygonIndex = 0; + + for (let j = 0; j <= cellCountV; j++) { + for (let i = 0; i <= cellCountU; i++) { + pointsFloat32Arr[pointIndex++] = interpolatedPointsXY[i].x; + pointsFloat32Arr[pointIndex++] = interpolatedPointsXY[i].y; + pointsFloat32Arr[pointIndex++] = minDepth + j * depthIncrement; + + if (i < cellCountU && j < cellCountV) { + polysUint32Arr[polygonIndex++] = 4; + polysUint32Arr[polygonIndex++] = i + j * (cellCountU + 1); + polysUint32Arr[polygonIndex++] = i + 1 + j * (cellCountU + 1); + polysUint32Arr[polygonIndex++] = i + 1 + (j + 1) * (cellCountU + 1); + polysUint32Arr[polygonIndex++] = i + (j + 1) * (cellCountU + 1); + } + } + } + const propertyArr = seismicCrosslineData.dataFloat32Arr; + const transposedPropertyArr = []; + for (let j = 0; j < depthSamples; j++) { + for (let i = 0; i < cellCountU; i++) { + transposedPropertyArr.push(propertyArr[i * depthSamples + j]); + } + } + const chunkSize = 10000; + let minValue = Infinity; + let maxValue = -Infinity; + + for (let i = 0; i < transposedPropertyArr.length; i += chunkSize) { + const chunk = transposedPropertyArr.slice(i, i + chunkSize); + const chunkMin = Math.min(...chunk); + const chunkMax = Math.max(...chunk); + + if (chunkMin < minValue) minValue = chunkMin; + if (chunkMax > maxValue) maxValue = chunkMax; + } + return { + pointsFloat32Arr, + polysUint32Arr, + propertyFloat32Arr: new Float32Array(transposedPropertyArr), + minValue, + maxValue, + }; +} diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting.tsx new file mode 100644 index 000000000..8bdbc7fe7 --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting.tsx @@ -0,0 +1,53 @@ +import React from "react"; + +import { Dropdown, DropdownOption } from "@lib/components/Dropdown"; + +import { SettingDelegate } from "../../delegates/SettingDelegate"; +import { Setting, SettingComponentProps } from "../../interfaces"; +import { SettingRegistry } from "../SettingRegistry"; +import { SettingType } from "../settingsTypes"; + +type ValueType = string | null; + +export class SeismicAttributeSetting implements Setting { + private _delegate: SettingDelegate; + + constructor() { + this._delegate = new SettingDelegate(null, this); + } + + getType(): SettingType { + return SettingType.SEISMIC_ATTRIBUTE; + } + + getLabel(): string { + return "Seismic Attribute"; + } + + getDelegate(): SettingDelegate { + return this._delegate; + } + + makeComponent(): (props: SettingComponentProps) => React.ReactNode { + return function Ensemble(props: SettingComponentProps) { + const options: DropdownOption[] = props.availableValues.map((value) => { + return { + value: value.toString(), + label: value === null ? "None" : value.toString(), + }; + }); + + return ( + + ); + }; + } +} + +SettingRegistry.registerSetting(SeismicAttributeSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx new file mode 100644 index 000000000..632fe975f --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx @@ -0,0 +1,93 @@ +import React from "react"; + +import { Slider } from "@lib/components/Slider"; + +import { SettingDelegate } from "../../delegates/SettingDelegate"; +import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; +import { SettingRegistry } from "../SettingRegistry"; +import { SettingType } from "../settingsTypes"; + +type ValueType = number | null; + +export class SeismicCrosslineSetting implements Setting { + private _delegate: SettingDelegate; + + constructor() { + this._delegate = new SettingDelegate(null, this); + } + + getType(): SettingType { + return SettingType.SEISMIC_CROSSLINE; + } + + getLabel(): string { + return "Seismic Crossline number"; + } + + getDelegate(): SettingDelegate { + return this._delegate; + } + + isValueValid(availableValues: AvailableValuesType, value: ValueType): boolean { + if (value === null) { + return false; + } + + if (availableValues.length < 2) { + return false; + } + + const min = 0; + const max = availableValues[1]; + + if (max === null) { + return false; + } + + return value >= min && value <= max; + } + + fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { + if (availableValues.length < 2) { + return null; + } + + const min = availableValues[1]; + const max = availableValues[1]; + + if (max === null) { + return null; + } + + if (currentValue === null) { + return min; + } + + return Math.min(Math.max(currentValue, min), max); + } + + makeComponent(): (props: SettingComponentProps) => React.ReactNode { + return function KRangeSlider(props: SettingComponentProps) { + function handleChange(_: any, value: number | number[]) { + if (Array.isArray(value)) { + return value[0]; + } + + props.onValueChange(value); + } + + return ( + + ); + }; + } +} + +SettingRegistry.registerSetting(SeismicCrosslineSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx new file mode 100644 index 000000000..470c97bf6 --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx @@ -0,0 +1,93 @@ +import React from "react"; + +import { Slider } from "@lib/components/Slider"; + +import { SettingDelegate } from "../../delegates/SettingDelegate"; +import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; +import { SettingRegistry } from "../SettingRegistry"; +import { SettingType } from "../settingsTypes"; + +type ValueType = number | null; + +export class SeismicInlineSetting implements Setting { + private _delegate: SettingDelegate; + + constructor() { + this._delegate = new SettingDelegate(null, this); + } + + getType(): SettingType { + return SettingType.SEISMIC_INLINE; + } + + getLabel(): string { + return "Seismic inline number"; + } + + getDelegate(): SettingDelegate { + return this._delegate; + } + + isValueValid(availableValues: AvailableValuesType, value: ValueType): boolean { + if (value === null) { + return false; + } + + if (availableValues.length < 2) { + return false; + } + + const min = 0; + const max = availableValues[1]; + + if (max === null) { + return false; + } + + return value >= min && value <= max; + } + + fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { + if (availableValues.length < 2) { + return null; + } + + const min = availableValues[1]; + const max = availableValues[1]; + + if (max === null) { + return null; + } + + if (currentValue === null) { + return min; + } + + return Math.min(Math.max(currentValue, min), max); + } + + makeComponent(): (props: SettingComponentProps) => React.ReactNode { + return function KRangeSlider(props: SettingComponentProps) { + function handleChange(_: any, value: number | number[]) { + if (Array.isArray(value)) { + return value[0]; + } + + props.onValueChange(value); + } + + return ( + + ); + }; + } +} + +SettingRegistry.registerSetting(SeismicInlineSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts index 574f2e304..94e5702b7 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts +++ b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts @@ -16,4 +16,7 @@ export enum SettingType { GRID_LAYER_K_RANGE = "gridLayerKRange", GRID_LAYER_K = "gridLayerK", SHOW_GRID_LINES = "showGridLines", + SEISMIC_ATTRIBUTE = "seismicAttribute", + SEISMIC_INLINE = "seismicInline", + SEISMIC_CROSSLINE = "seismicCrossline", } From 03012a6c7e6e2ab75372fc4cc3342588df9b40ed Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:39:44 +0100 Subject: [PATCH 19/97] fix seismic settings --- .../SeismicCrosslineSetting.tsx | 33 ++++++++++++------- .../implementations/SeismicInlineSetting.tsx | 33 ++++++++++++------- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx index 632fe975f..8e3eecf69 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx @@ -1,5 +1,6 @@ import React from "react"; +import { Input } from "@lib/components/Input"; import { Slider } from "@lib/components/Slider"; import { SettingDelegate } from "../../delegates/SettingDelegate"; @@ -21,7 +22,7 @@ export class SeismicCrosslineSetting implements Setting { } getLabel(): string { - return "Seismic Crossline number"; + return "Seismic crossline"; } getDelegate(): SettingDelegate { @@ -52,7 +53,7 @@ export class SeismicCrosslineSetting implements Setting { return null; } - const min = availableValues[1]; + const min = availableValues[0]; const max = availableValues[1]; if (max === null) { @@ -68,23 +69,33 @@ export class SeismicCrosslineSetting implements Setting { makeComponent(): (props: SettingComponentProps) => React.ReactNode { return function KRangeSlider(props: SettingComponentProps) { - function handleChange(_: any, value: number | number[]) { + function handleSliderChange(_: any, value: number | number[]) { if (Array.isArray(value)) { return value[0]; } props.onValueChange(value); } + function handleInputChange(event: React.ChangeEvent) { + props.onValueChange(Number(event.target.value)); + } return ( - +
+
+ +
+
+ +
+
); }; } diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx index 470c97bf6..5b3c61e93 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx @@ -1,5 +1,6 @@ import React from "react"; +import { Input } from "@lib/components/Input"; import { Slider } from "@lib/components/Slider"; import { SettingDelegate } from "../../delegates/SettingDelegate"; @@ -21,7 +22,7 @@ export class SeismicInlineSetting implements Setting { } getLabel(): string { - return "Seismic inline number"; + return "Seismic inline"; } getDelegate(): SettingDelegate { @@ -52,7 +53,7 @@ export class SeismicInlineSetting implements Setting { return null; } - const min = availableValues[1]; + const min = availableValues[0]; const max = availableValues[1]; if (max === null) { @@ -68,23 +69,33 @@ export class SeismicInlineSetting implements Setting { makeComponent(): (props: SettingComponentProps) => React.ReactNode { return function KRangeSlider(props: SettingComponentProps) { - function handleChange(_: any, value: number | number[]) { + function handleSliderChange(_: any, value: number | number[]) { if (Array.isArray(value)) { return value[0]; } props.onValueChange(value); } + function handleInputChange(event: React.ChangeEvent) { + props.onValueChange(Number(event.target.value)); + } return ( - +
+
+ +
+
+ +
+
); }; } From a322c038152218c5b5c011a566780f677cd76643 Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Fri, 24 Jan 2025 16:43:31 +0100 Subject: [PATCH 20/97] Add tvdmsl to well picks --- .../3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts b/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts index 4c6ddd195..fe97dfa76 100644 --- a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts @@ -41,7 +41,7 @@ export class WellborePicksLayer extends CompositeLayer type: "Feature", geometry: { type: "Point", - coordinates: [wellPick.easting, wellPick.northing], + coordinates: [wellPick.easting, wellPick.northing, -wellPick.tvdMsl], }, properties: { name: `${wellPick.wellBoreUwi}, TVD_MSL: ${wellPick.tvdMsl}, MD: ${wellPick.md}`, @@ -57,7 +57,7 @@ export class WellborePicksLayer extends CompositeLayer const textData: TextLayerData[] = this.props.data.map((wellPick) => { return { - coordinates: [wellPick.easting, wellPick.northing, wellPick.tvdMsl], + coordinates: [wellPick.easting, wellPick.northing, -wellPick.tvdMsl], name: wellPick.wellBoreUwi, }; }); From 953c0259774da1f08565354794292af64285f34c Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Fri, 24 Jan 2025 16:44:04 +0100 Subject: [PATCH 21/97] Add seismic depth slice layer --- .../primary/routers/seismic/converters.py | 94 ++++++++++- .../primary/primary/routers/seismic/router.py | 41 ++++- .../primary/routers/seismic/schemas.py | 1 + .../services/sumo_access/seismic_access.py | 1 + .../services/sumo_access/seismic_types.py | 1 + .../services/vds_access/request_types.py | 1 + .../primary/services/vds_access/vds_access.py | 38 +++++ .../api/autogen/@tanstack/react-query.gen.ts | 21 +++ frontend/src/api/autogen/sdk.gen.ts | 26 ++- frontend/src/api/autogen/types.gen.ts | 55 +++++++ .../RealizationSeismicCrosslineLayer.ts | 2 +- .../RealizationSeismicDepthSliceLayer.ts | 126 +++++++++++++++ ...izationSeismicDepthSliceSettingsContext.ts | 151 ++++++++++++++++++ .../index.ts | 2 + .../types.ts | 10 ++ .../RealizationSeismicInlineLayer.ts | 2 +- .../layerManagerComponentWrapper.tsx | 47 ++++-- .../3DViewerNew/view/utils/layerFactory.ts | 60 ++++++- .../SeismicDepthSliceSetting.tsx | 117 ++++++++++++++ .../LayerFramework/settings/settingsTypes.ts | 1 + 20 files changed, 772 insertions(+), 25 deletions(-) create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/index.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts create mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicDepthSliceSetting.tsx diff --git a/backend_py/primary/primary/routers/seismic/converters.py b/backend_py/primary/primary/routers/seismic/converters.py index 9583fed50..8819d4b41 100644 --- a/backend_py/primary/primary/routers/seismic/converters.py +++ b/backend_py/primary/primary/routers/seismic/converters.py @@ -1,4 +1,5 @@ from typing import List +import math import orjson import numpy as np @@ -8,8 +9,8 @@ from webviz_pkg.core_utils.b64 import b64_encode_float_array_as_float32 from primary.services.vds_access.response_types import VdsSliceMetadata from . import schemas - - +from ..surface import schemas as surface_schemas +from primary.services.utils.surface_to_float32 import surface_to_float32_numpy_array def surface_to_float32_array(values: np.ndarray) -> List[float]: values = values.astype(np.float32) @@ -99,4 +100,91 @@ def to_api_vds_crossline_data( value_min=np.nanmin(flattened_slice_traces_array), value_max=np.nanmax(flattened_slice_traces_array), - ) \ No newline at end of file + ) +def to_api_vds_depth_slice_data( + flattened_slice_traces_array: NDArray[np.float32], metadata: VdsSliceMetadata) -> surface_schemas.SurfaceDataFloat: + """ + Create API SeismicCrosslineData from VdsSliceMetadata and flattened slice traces array + """ + # VdsSliceMetadata(format=' surface_schemas.SurfaceDataFloat: + """Get a seismic depth slice from a seismic cube. + """ + seismic_access = await SeismicAccess.from_case_uuid_async( + authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name + ) + vds_handle: Optional[VdsHandle] = None + try: + vds_handle = await seismic_access.get_vds_handle_async( + realization=realization_num, + seismic_attribute=seismic_attribute, + time_or_interval_str=time_or_interval_str, + observed=observed, + ) + except ValueError as err: + raise HTTPException(status_code=404, detail=str(err)) from err + + if vds_handle is None: + raise HTTPException(status_code=404, detail="Vds handle not found") + + vds_access = VdsAccess(sas_token=vds_handle.sas_token, vds_url=vds_handle.vds_url) + + flattened_slice_traces_array,metadata = await vds_access.get_depth_slice( depth=depth) + + return converters.to_api_vds_depth_slice_data(flattened_slice_traces_array=flattened_slice_traces_array, + metadata=metadata + ) @router.post("/get_seismic_fence/") async def post_get_seismic_fence( authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), diff --git a/backend_py/primary/primary/routers/seismic/schemas.py b/backend_py/primary/primary/routers/seismic/schemas.py index 9a1dda2b5..20797f00e 100644 --- a/backend_py/primary/primary/routers/seismic/schemas.py +++ b/backend_py/primary/primary/routers/seismic/schemas.py @@ -18,6 +18,7 @@ class SeismicCubeMeta(BaseModel): k_max: int z_min: float z_max: float + z_inc: float class SeismicFencePolyline(BaseModel): """ diff --git a/backend_py/primary/primary/services/sumo_access/seismic_access.py b/backend_py/primary/primary/services/sumo_access/seismic_access.py index eba47c63a..7ba0f6b9c 100644 --- a/backend_py/primary/primary/services/sumo_access/seismic_access.py +++ b/backend_py/primary/primary/services/sumo_access/seismic_access.py @@ -55,6 +55,7 @@ async def get_seismic_cube_meta_list_async(self) -> List[SeismicCubeMeta]: k_max=cube["data"]["spec"]["nlay"]-1, z_min=cube["data"]["bbox"]["zmin"], z_max=cube["data"]["bbox"]["zmax"], + z_inc=cube["data"]["spec"]["zinc"], ) seismic_cube_meta_list.append(seismic_meta) return seismic_cube_meta_list diff --git a/backend_py/primary/primary/services/sumo_access/seismic_types.py b/backend_py/primary/primary/services/sumo_access/seismic_types.py index f22650885..24a0d6791 100644 --- a/backend_py/primary/primary/services/sumo_access/seismic_types.py +++ b/backend_py/primary/primary/services/sumo_access/seismic_types.py @@ -14,6 +14,7 @@ class SeismicCubeMeta(BaseModel): k_max: int z_min: float z_max: float + z_inc: float class VdsHandle(BaseModel): diff --git a/backend_py/primary/primary/services/vds_access/request_types.py b/backend_py/primary/primary/services/vds_access/request_types.py index b9e9384c9..617fbac2c 100644 --- a/backend_py/primary/primary/services/vds_access/request_types.py +++ b/backend_py/primary/primary/services/vds_access/request_types.py @@ -32,6 +32,7 @@ class VdsDirection(StrEnum): INLINE = "inline" CROSSLINE = "crossline" SAMPLE = "sample" + DEPTH = "depth" class VdsCoordinateSystem(StrEnum): """ diff --git a/backend_py/primary/primary/services/vds_access/vds_access.py b/backend_py/primary/primary/services/vds_access/vds_access.py index 95f29b6a9..8a5de627d 100644 --- a/backend_py/primary/primary/services/vds_access/vds_access.py +++ b/backend_py/primary/primary/services/vds_access/vds_access.py @@ -158,6 +158,44 @@ async def get_crossline_slice(self,line_no:int ) -> Tuple[NDArray[np.float32], V return (flattened_fence_traces_float32_array, metadata) + async def get_depth_slice(self,depth:float ) -> Tuple[NDArray[np.float32], VdsSliceMetadata]: + endpoint = "slice" + hard_coded_fill_value = -999.25 + slice_request = VdsSliceRequest( + vds=self.vds_url, + sas=self.sas, + direction=VdsDirection.DEPTH, + line_no=depth, + ) + response = await self._query_async(endpoint, slice_request) + decoder = MultipartDecoder(content=response.content, content_type=response.headers["Content-Type"]) + parts = decoder.parts + # Validate parts from decoded response + if len(parts) != 2 or not parts[0].content or not parts[1].content: + raise ValueError(f"Expected two parts, got {len(parts)}") + + # Expect each part in parts tuple to be BodyPart + if not isinstance(parts[0], BodyPart) or not isinstance(parts[1], BodyPart): + raise ValueError(f"Expected parts to be BodyPart, got {type(parts[0])}, {type(parts[1])}") + + metadata = VdsSliceMetadata(**json.loads(parts[0].content)) + byte_array = parts[1].content + + if metadata.format != " Tuple[NDArray[np.float32], int, int]: diff --git a/frontend/src/api/autogen/@tanstack/react-query.gen.ts b/frontend/src/api/autogen/@tanstack/react-query.gen.ts index 208ace2d7..9757fd023 100644 --- a/frontend/src/api/autogen/@tanstack/react-query.gen.ts +++ b/frontend/src/api/autogen/@tanstack/react-query.gen.ts @@ -15,6 +15,7 @@ import { getDeltaEnsembleStatisticalVectorData, getDeltaEnsembleVectorList, getDeltaSurfaceData, + getDepthSlice, getDrilledWellboreHeaders, getEnsembleDetails, getEnsembles, @@ -84,6 +85,7 @@ import type { GetDeltaEnsembleStatisticalVectorDataData_api, GetDeltaEnsembleVectorListData_api, GetDeltaSurfaceDataData_api, + GetDepthSliceData_api, GetDrilledWellboreHeadersData_api, GetEnsembleDetailsData_api, GetEnsemblesData_api, @@ -1272,6 +1274,25 @@ export const getCrosslineSliceOptions = (options: Options) => [ + createQueryKey("getDepthSlice", options), +]; + +export const getDepthSliceOptions = (options: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getDepthSlice({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }); + return data; + }, + queryKey: getDepthSliceQueryKey(options), + }); +}; + export const postGetSeismicFenceQueryKey = (options: Options) => [ createQueryKey("postGetSeismicFence", options), ]; diff --git a/frontend/src/api/autogen/sdk.gen.ts b/frontend/src/api/autogen/sdk.gen.ts index 2720ea98f..0fd2e60d7 100644 --- a/frontend/src/api/autogen/sdk.gen.ts +++ b/frontend/src/api/autogen/sdk.gen.ts @@ -25,6 +25,9 @@ import type { GetDeltaSurfaceDataData_api, GetDeltaSurfaceDataError_api, GetDeltaSurfaceDataResponse_api, + GetDepthSliceData_api, + GetDepthSliceError_api, + GetDepthSliceResponse_api, GetDrilledWellboreHeadersData_api, GetDrilledWellboreHeadersError_api, GetDrilledWellboreHeadersResponse_api, @@ -998,10 +1001,7 @@ export const getSeismicCubeMetaList = ( /** * Get Inline Slice - * Get a seismic slice from a seismic cube. - * - * Returns: - * A SeismicFenceData object with fence traces in encoded 1D array, metadata for trace array decoding and fence min/max depth. + * Get a seismic inline from a seismic cube. */ export const getInlineSlice = ( options: Options @@ -1014,10 +1014,7 @@ export const getInlineSlice = ( /** * Get Crossline Slice - * Get a seismic slice from a seismic cube. - * - * Returns: - * A SeismicFenceData object with fence traces in encoded 1D array, metadata for trace array decoding and fence min/max depth. + * Get a seismic crossline from a seismic cube. */ export const getCrosslineSlice = ( options: Options @@ -1028,6 +1025,19 @@ export const getCrosslineSlice = ( }); }; +/** + * Get Depth Slice + * Get a seismic depth slice from a seismic cube. + */ +export const getDepthSlice = ( + options: Options +) => { + return (options?.client ?? client).get({ + ...options, + url: "/seismic/get_depth_slice/", + }); +}; + /** * Post Get Seismic Fence * Get a fence of seismic data from a polyline defined by a set of (x, y) coordinates in domain coordinate system. diff --git a/frontend/src/api/autogen/types.gen.ts b/frontend/src/api/autogen/types.gen.ts index 4a021b9dc..c65286159 100644 --- a/frontend/src/api/autogen/types.gen.ts +++ b/frontend/src/api/autogen/types.gen.ts @@ -574,6 +574,7 @@ export type SeismicCubeMeta_api = { k_max: number; z_min: number; z_max: number; + z_inc: number; }; /** @@ -3222,6 +3223,60 @@ export type GetCrosslineSliceResponses_api = { export type GetCrosslineSliceResponse_api = GetCrosslineSliceResponses_api[keyof GetCrosslineSliceResponses_api]; +export type GetDepthSliceData_api = { + body?: never; + path?: never; + query: { + /** + * Sumo case uuid + */ + case_uuid: string; + /** + * Ensemble name + */ + ensemble_name: string; + /** + * Realization number + */ + realization_num: number; + /** + * Seismic cube attribute + */ + seismic_attribute: string; + /** + * Timestamp or timestep + */ + time_or_interval_str: string; + /** + * Observed or simulated + */ + observed: boolean; + /** + * depth + */ + depth: number; + }; + url: "/seismic/get_depth_slice/"; +}; + +export type GetDepthSliceErrors_api = { + /** + * Validation Error + */ + 422: HttpValidationError_api; +}; + +export type GetDepthSliceError_api = GetDepthSliceErrors_api[keyof GetDepthSliceErrors_api]; + +export type GetDepthSliceResponses_api = { + /** + * Successful Response + */ + 200: SurfaceDataFloat_api; +}; + +export type GetDepthSliceResponse_api = GetDepthSliceResponses_api[keyof GetDepthSliceResponses_api]; + export type PostGetSeismicFenceData_api = { body: BodyPostGetSeismicFence_api; path?: never; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index 86530006e..4c04bad4f 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -21,7 +21,7 @@ export class RealizationSeismicCrosslineLayer private _itemDelegate: ItemDelegate; constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Realization Seismic Crossline", layerManager); + this._itemDelegate = new ItemDelegate("Seismic Crossline (realization)", layerManager); this._layerDelegate = new LayerDelegate( this, layerManager, diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts new file mode 100644 index 000000000..0d4a4153a --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts @@ -0,0 +1,126 @@ +import { SurfaceDataPng_api, getCrosslineSliceOptions, getDepthSliceOptions } from "@api"; +import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; +import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; +import { SurfaceDataFloat_trans, transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; +import { QueryClient } from "@tanstack/react-query"; + +import { isEqual } from "lodash"; + +import { RealizationSeismicDepthSliceSettingsContext } from "./RealizationSeismicDepthSliceSettingsContext"; +import { RealizationSeismicDepthSliceSettings } from "./types"; + +import { SeismicCrosslineData_trans, transformSeismicCrossline } from "../../../settings/queries/queryDataTransforms"; + +export class RealizationSeismicDepthSliceLayer + implements Layer +{ + private _layerDelegate: LayerDelegate< + RealizationSeismicDepthSliceSettings, + SurfaceDataFloat_trans | SurfaceDataPng_api + >; + private _itemDelegate: ItemDelegate; + + constructor(layerManager: LayerManager) { + this._itemDelegate = new ItemDelegate("Seismic depth slice (realization)", layerManager); + this._layerDelegate = new LayerDelegate( + this, + layerManager, + new RealizationSeismicDepthSliceSettingsContext(layerManager), + LayerColoringType.COLORSCALE + ); + } + + getSettingsContext() { + return this._layerDelegate.getSettingsContext(); + } + + getItemDelegate(): ItemDelegate { + return this._itemDelegate; + } + + getLayerDelegate(): LayerDelegate< + RealizationSeismicDepthSliceSettings, + SurfaceDataFloat_trans | SurfaceDataPng_api + > { + return this._layerDelegate; + } + makeBoundingBox(): BoundingBox | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return { + x: [data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.max_x], + y: [data.transformed_bbox_utm.min_y, data.transformed_bbox_utm.max_y], + z: [data.value_min, data.value_max], + }; + } + doSettingsChangesRequireDataRefetch( + prevSettings: RealizationSeismicDepthSliceSettings, + newSettings: RealizationSeismicDepthSliceSettings + ): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeValueRange(): [number, number] | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + return [data.value_min, data.value_max]; + } + + fetchData(queryClient: QueryClient): Promise { + const settings = this.getSettingsContext().getDelegate().getSettings(); + const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); + const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); + const seismicAttribute = settings[SettingType.SEISMIC_ATTRIBUTE].getDelegate().getValue(); + + let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + const seismicDepth = settings[SettingType.SEISMIC_DEPTH_SLICE].getDelegate().getValue(); + + const queryKey = [ + "RealizationSeismicDepthSliceSlice", + ensembleIdent, + seismicAttribute, + timeOrInterval, + realizationNum, + seismicDepth, + ]; + this._layerDelegate.registerQueryKey(queryKey); + + const seismicSlicePromise = queryClient + .fetchQuery({ + ...getDepthSliceOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + realization_num: realizationNum ?? 0, + seismic_attribute: seismicAttribute ?? "", + time_or_interval_str: timeOrInterval ?? "", + observed: false, + depth: seismicDepth ?? 0, + }, + }), + }) + .then((data) => transformSurfaceData(data)); + + return seismicSlicePromise; + } + + serializeState(): SerializedLayer { + return this._layerDelegate.serializeState(); + } + + deserializeState(serializedState: SerializedLayer): void { + this._layerDelegate.deserializeState(serializedState); + } +} + +LayerRegistry.registerLayer(RealizationSeismicDepthSliceLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts new file mode 100644 index 000000000..4590d384e --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts @@ -0,0 +1,151 @@ +import { getSeismicCubeMetaListOptions } from "@api"; +import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; +import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; +import { SeismicAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting"; +import { SeismicDepthSliceSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicDepthSliceSetting"; +import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +import { RealizationSeismicDepthSliceSettings } from "./types"; + +export class RealizationSeismicDepthSliceSettingsContext + implements SettingsContext +{ + private _contextDelegate: SettingsContextDelegate; + + constructor(layerManager: LayerManager) { + this._contextDelegate = new SettingsContextDelegate< + RealizationSeismicDepthSliceSettings, + keyof RealizationSeismicDepthSliceSettings + >(this, layerManager, { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.REALIZATION]: new RealizationSetting(), + [SettingType.SEISMIC_ATTRIBUTE]: new SeismicAttributeSetting(), + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + [SettingType.SEISMIC_DEPTH_SLICE]: new SeismicDepthSliceSetting(), + }); + } + + areCurrentSettingsValid(settings: RealizationSeismicDepthSliceSettings): boolean { + return ( + settings[SettingType.ENSEMBLE] !== null && + settings[SettingType.REALIZATION] !== null && + settings[SettingType.SEISMIC_ATTRIBUTE] !== null && + settings[SettingType.TIME_OR_INTERVAL] !== null + ); + } + + getDelegate(): SettingsContextDelegate { + return this._contextDelegate; + } + + getSettings() { + return this._contextDelegate.getSettings(); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + queryClient, + }: DefineDependenciesArgs) { + availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); + + if (!ensembleIdent) { + return []; + } + + const realizations = realizationFilterFunc(ensembleIdent); + + return [...realizations]; + }); + const RealizationSeismicDepthSliceDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realization = getLocalSetting(SettingType.REALIZATION); + + if (!ensembleIdent || realization === null) { + return null; + } + + return await queryClient.fetchQuery({ + ...getSeismicCubeMetaListOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + }, + signal: abortSignal, + }), + }); + }); + + availableSettingsUpdater(SettingType.SEISMIC_ATTRIBUTE, ({ getHelperDependency }) => { + const data = getHelperDependency(RealizationSeismicDepthSliceDataDep); + + if (!data) { + return []; + } + + const availableSeismicAttributes = [ + ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismic_attribute))), + ]; + + return availableSeismicAttributes; + }); + + availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + + const data = getHelperDependency(RealizationSeismicDepthSliceDataDep); + + if (!seismicAttribute || !data) { + return []; + } + + const availableTimeOrIntervals = [ + ...Array.from( + new Set( + data + .filter((surface) => surface.seismic_attribute === seismicAttribute) + .map((el) => el.iso_date_or_interval) + ) + ), + ]; + + return availableTimeOrIntervals; + }); + availableSettingsUpdater(SettingType.SEISMIC_DEPTH_SLICE, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + const timeOrInterval = getLocalSetting(SettingType.TIME_OR_INTERVAL); + const data = getHelperDependency(RealizationSeismicDepthSliceDataDep); + + if (!seismicAttribute || !timeOrInterval || !data) { + return []; + } + const seismicInfo = data.filter( + (seismicInfos) => + seismicInfos.seismic_attribute === seismicAttribute && + seismicInfos.iso_date_or_interval === timeOrInterval + )[0]; + const zMin = seismicInfo.z_min; + const zMax = seismicInfo.z_max; + const zInc = seismicInfo.z_inc; + + return [zMin, zMax, zInc]; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/index.ts new file mode 100644 index 000000000..abd4a3635 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/index.ts @@ -0,0 +1,2 @@ +export { RealizationSeismicDepthSliceLayer } from "./RealizationSeismicDepthSliceLayer"; +export { RealizationSeismicDepthSliceSettingsContext } from "./RealizationSeismicDepthSliceSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts new file mode 100644 index 000000000..c0eb9be86 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts @@ -0,0 +1,10 @@ +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +export type RealizationSeismicDepthSliceSettings = { + [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; + [SettingType.REALIZATION]: number | null; + [SettingType.SEISMIC_ATTRIBUTE]: string | null; + [SettingType.TIME_OR_INTERVAL]: string | null; + [SettingType.SEISMIC_DEPTH_SLICE]: number | null; +}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts index c2602fb7f..e0f9a9c8f 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts @@ -19,7 +19,7 @@ export class RealizationSeismicInlineLayer implements Layer, label: "Realization Grid", }, + ], + }, + { + label: "Surface", + children: [ { identifier: "realization-surface", icon: , label: "Realization Surface", }, + ], + }, + { + label: "Seismic", + children: [ { - identifier: "realization-seismic-inline", - icon: , - label: "Realization Seismic Inline", - }, - { - identifier: "realization-seismic-crossline", - icon: , - label: "Realization Seismic Crossline", + label: "Synthetic", + children: [ + { + identifier: "realization-seismic-inline", + icon: , + label: "Realization Inline", + }, + { + identifier: "realization-seismic-crossline", + icon: , + label: "Realization Crossline", + }, + { + identifier: "realization-seismic-depth-slice", + icon: , + label: "Realization Depth Slice", + }, + ], }, ], }, diff --git a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts index d0374347f..c6a3dd04d 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts @@ -1,9 +1,10 @@ -import { WellborePick_api, WellboreTrajectory_api } from "@api"; +import { SurfaceDef_api, WellborePick_api, WellboreTrajectory_api } from "@api"; import { Layer } from "@deck.gl/core"; import { defaultColorPalettes } from "@framework/utils/colorPalettes"; import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; import { GridMappedProperty_trans, GridSurface_trans } from "@modules/3DViewer/view/queries/queryDataTransforms"; import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; +import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer"; import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; import { SeismicCrosslineData_trans, @@ -114,6 +115,26 @@ export function makeDeckGlLayer(layer: LayerInterface, colorScale?: Co }); return grid3dLayer as unknown as WorkingGrid3dLayer; } + if (layer instanceof RealizationSeismicDepthSliceLayer) { + const zValue = layer + .getSettingsContext() + .getDelegate() + .getSettings() + .seismicDepthSlice.getDelegate() + .getValue(); + const layerData: SurfaceDataFloat_trans = data; + return createSurfaceConstantZWIthPropertyLayer({ + surfaceDef: layerData.surface_def, + propertyFloat32Arr: layerData.valuesFloat32Arr, + zValue: zValue || 0, + id: layer.getItemDelegate().getId(), + name: layer.getItemDelegate().getName(), + valueMin: layerData.value_min, + valueMax: layerData.value_max, + colorScale: colorScale, + showGridLines: false, + }); + } return null; } @@ -153,6 +174,43 @@ export function createSurfaceMeshLayer( colorMapFunction: makeColorMapFunction(colorScale, layerData.value_min, layerData.value_max), }); } +type SurfaceConstantZWithPropertyLayerOptions = { + surfaceDef: SurfaceDef_api; + propertyFloat32Arr: Float32Array; + zValue: number; + id: string; + name: string; + valueMin: number; + valueMax: number; + colorScale: ColorScaleWithName; + showGridLines: boolean; +}; +export function createSurfaceConstantZWIthPropertyLayer(options: SurfaceConstantZWithPropertyLayerOptions): MapLayer { + const meshData = new Float32Array(options.propertyFloat32Arr.length); + meshData.fill(options.zValue); + return new MapLayer({ + "@@type": "MapLayer", + "@@typedArraySupport": true, + id: options.id, + name: options.name, + meshData: meshData, + propertiesData: options.propertyFloat32Arr, + frame: { + origin: [options.surfaceDef.origin_utm_x, options.surfaceDef.origin_utm_y], + count: [options.surfaceDef.npoints_x, options.surfaceDef.npoints_y], + increment: [options.surfaceDef.inc_x, options.surfaceDef.inc_y], + rotDeg: options.surfaceDef.rot_deg, + }, + valueRange: [options.valueMin, options.valueMax], + colorMapRange: [options.valueMin, options.valueMax], + gridLines: options.showGridLines, + material: false, + smoothShading: false, + colorMapName: "Physics", + + colorMapFunction: makeColorMapFunction(options.colorScale, options.valueMin, options.valueMax), + }); +} function createWellPicksLayer(wellPicksDataApi: WellborePick_api[], id: string): WellborePicksLayer { const wellPicksData: WellBorePickLayerData[] = wellPicksDataApi.map((wellPick) => { return { diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicDepthSliceSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicDepthSliceSetting.tsx new file mode 100644 index 000000000..7b3eafacb --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicDepthSliceSetting.tsx @@ -0,0 +1,117 @@ +import React from "react"; + +import { Input } from "@lib/components/Input"; +import { Slider } from "@lib/components/Slider"; + +import { SettingDelegate } from "../../delegates/SettingDelegate"; +import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; +import { SettingRegistry } from "../SettingRegistry"; +import { SettingType } from "../settingsTypes"; + +type ValueType = number | null; + +export class SeismicDepthSliceSetting implements Setting { + private _delegate: SettingDelegate; + + constructor() { + this._delegate = new SettingDelegate(null, this); + } + + getType(): SettingType { + return SettingType.SEISMIC_DEPTH_SLICE; + } + + getLabel(): string { + return "Seismic depth"; + } + + getDelegate(): SettingDelegate { + return this._delegate; + } + + isValueValid(availableValues: AvailableValuesType, value: ValueType): boolean { + if (value === null) { + return false; + } + + if (availableValues.length < 2) { + return false; + } + + const min = 0; + const max = availableValues[1]; + + if (max === null) { + return false; + } + + return value >= min && value <= max; + } + + fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { + if (availableValues.length < 3) { + return null; + } + + const min = availableValues[0]; + const max = availableValues[1]; + + if (max === null) { + return null; + } + + if (currentValue === null) { + return min; + } + + return Math.min(Math.max(currentValue, min), max); + } + + makeComponent(): (props: SettingComponentProps) => React.ReactNode { + return function KRangeSlider(props: SettingComponentProps) { + function handleSliderChange(_: any, value: number | number[]) { + if (Array.isArray(value)) { + return value[0]; + } + + props.onValueChange(value); + } + function handleInputChange(event: React.ChangeEvent) { + // Check if value is allowed (in increments of availableValues[2], if not return closest allowed value) + const value = Number(event.target.value); + const min = props.availableValues[0]; + const max = props.availableValues[1]; + const step = props.availableValues[2]; + const allowedValues = Array.from( + { length: Math.floor((max - min) / step) + 1 }, + (_, i) => min + i * step + ); + const closestValue = allowedValues.reduce((prev, curr) => + Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev + ); + props.onValueChange(closestValue); + } + + return ( +
+
+ +
+
+ +
+
+ ); + }; + } +} + +SettingRegistry.registerSetting(SeismicDepthSliceSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts index 94e5702b7..f747c9c42 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts +++ b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts @@ -19,4 +19,5 @@ export enum SettingType { SEISMIC_ATTRIBUTE = "seismicAttribute", SEISMIC_INLINE = "seismicInline", SEISMIC_CROSSLINE = "seismicCrossline", + SEISMIC_DEPTH_SLICE = "seismicDepthSlice", } From ab4c70d431f5230fbfcbb6d04bb276695c766131 Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Sat, 25 Jan 2025 19:03:36 +0100 Subject: [PATCH 22/97] set inputs as number --- .../settings/implementations/SeismicCrosslineSetting.tsx | 2 +- .../settings/implementations/SeismicInlineSetting.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx index 8e3eecf69..11aaa15ff 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx @@ -93,7 +93,7 @@ export class SeismicCrosslineSetting implements Setting { />
- +
); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx index 5b3c61e93..b6dfa52e4 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx @@ -93,7 +93,7 @@ export class SeismicInlineSetting implements Setting { />
- +
); From 32031f4aafc1956a8f851c2e87649505118580cc Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Mon, 27 Jan 2025 08:41:48 +0100 Subject: [PATCH 23/97] Use same attribute --- .../ObservedSurfaceLayer.ts | 2 +- .../ObservedSurfaceSettingsContext.ts | 10 ++-- .../ObservedSurfaceLayer/types.ts | 2 +- .../RealizationGridLayer.ts | 2 +- .../RealizationGridSettingsContext.ts | 10 ++-- .../RealizationGridLayer/types.ts | 2 +- .../RealizationSurfaceLayer.ts | 2 +- .../RealizationSurfaceSettingsContext.ts | 10 ++-- .../RealizationSurfaceLayer/types.ts | 2 +- .../StatisticalSurfaceLayer.ts | 2 +- .../StatisticalSurfaceSettingsContext.ts | 10 ++-- .../StatisticalSurfaceLayer/types.ts | 2 +- .../layerManagerComponentWrapper.tsx | 10 ++-- .../RealizationGridLayer.ts | 2 +- .../RealizationGridSettingsContext.ts | 10 ++-- .../RealizationGridLayer/types.ts | 2 +- .../RealizationSeismicCrosslineLayer.ts | 2 +- ...lizationSeismicCrosslineSettingsContext.ts | 20 +++---- .../RealizationSeismicCrosslineLayer/types.ts | 2 +- .../RealizationSeismicDepthSliceLayer.ts | 2 +- ...izationSeismicDepthSliceSettingsContext.ts | 12 ++--- .../types.ts | 2 +- .../RealizationSeismicInlineLayer.ts | 2 +- ...RealizationSeismicInlineSettingsContext.ts | 21 +++----- .../RealizationSeismicInlineLayer/types.ts | 2 +- .../RealizationSurfaceLayer.ts | 2 +- .../RealizationSurfaceSettingsContext.ts | 10 ++-- .../RealizationSurfaceLayer/types.ts | 2 +- .../layerManagerComponentWrapper.tsx | 9 ++++ ...ributeSetting.tsx => AttributeSetting.tsx} | 8 +-- .../SeismicAttributeSetting.tsx | 53 ------------------- .../SurfaceAttributeSetting.tsx | 53 ------------------- .../LayerFramework/settings/settingsTypes.ts | 4 +- 33 files changed, 87 insertions(+), 199 deletions(-) rename frontend/src/modules/_shared/LayerFramework/settings/implementations/{GridAttributeSetting.tsx => AttributeSetting.tsx} (88%) delete mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting.tsx delete mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting.tsx diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts index 7d48713b8..8c839b2fe 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts @@ -79,7 +79,7 @@ export class ObservedSurfaceLayer const settings = this.getSettingsContext().getDelegate().getSettings(); const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); - const attribute = settings[SettingType.SURFACE_ATTRIBUTE].getDelegate().getValue(); + const attribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); if (ensembleIdent && surfaceName && attribute && timeOrInterval) { diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts index da58be5cf..3fd16c445 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts @@ -2,8 +2,8 @@ import { SurfaceTimeType_api, getObservedSurfacesMetadataOptions } from "@api"; import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; @@ -19,7 +19,7 @@ export class ObservedSurfaceSettingsContext implements SettingsContext { + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { const data = getHelperDependency(observedSurfaceMetadataDep); if (!data) { @@ -83,7 +83,7 @@ export class ObservedSurfaceSettingsContext implements SettingsContext { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const attribute = getLocalSetting(SettingType.ATTRIBUTE); const data = getHelperDependency(observedSurfaceMetadataDep); if (!attribute || !data) { @@ -102,7 +102,7 @@ export class ObservedSurfaceSettingsContext implements SettingsContext { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const attribute = getLocalSetting(SettingType.ATTRIBUTE); const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); const data = getHelperDependency(observedSurfaceMetadataDep); diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts index 37abb48dc..b3db4f576 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/types.ts @@ -3,7 +3,7 @@ import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTy export type ObservedSurfaceSettings = { [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; - [SettingType.SURFACE_ATTRIBUTE]: string | null; + [SettingType.ATTRIBUTE]: string | null; [SettingType.SURFACE_NAME]: string | null; [SettingType.TIME_OR_INTERVAL]: string | null; }; diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts index d65433fe3..8b0da89ba 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -108,7 +108,7 @@ export class RealizationGridLayer const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); const gridName = settings[SettingType.GRID_NAME].getDelegate().getValue(); - const attribute = settings[SettingType.GRID_ATTRIBUTE].getDelegate().getValue(); + const attribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); if (timeOrInterval === "NO_TIME") { timeOrInterval = null; diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts index 7ea7e872b..77d4b56d1 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts @@ -2,8 +2,8 @@ import { getGridModelsInfoOptions } from "@api"; import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { GridAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting"; import { GridLayerKSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKSetting"; import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; @@ -24,7 +24,7 @@ export class RealizationGridSettingsContext implements SettingsContext { + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getLocalSetting, getHelperDependency }) => { const gridName = getLocalSetting(SettingType.GRID_NAME); const data = getHelperDependency(realizationGridDataDep); @@ -150,7 +150,7 @@ export class RealizationGridSettingsContext implements SettingsContext { const gridName = getLocalSetting(SettingType.GRID_NAME); - const gridAttribute = getLocalSetting(SettingType.GRID_ATTRIBUTE); + const gridAttribute = getLocalSetting(SettingType.ATTRIBUTE); const data = getHelperDependency(realizationGridDataDep); if (!gridName || !gridAttribute || !data) { diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts index 51a41b25e..4bdc6fbd5 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts @@ -4,7 +4,7 @@ import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTy export type RealizationGridSettings = { [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; [SettingType.REALIZATION]: number | null; - [SettingType.GRID_ATTRIBUTE]: string | null; + [SettingType.ATTRIBUTE]: string | null; [SettingType.GRID_NAME]: string | null; [SettingType.GRID_LAYER_K]: number | null; [SettingType.TIME_OR_INTERVAL]: string | null; diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts index 52be9cf8c..75506edec 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts @@ -81,7 +81,7 @@ export class RealizationSurfaceLayer const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); - const attribute = settings[SettingType.SURFACE_ATTRIBUTE].getDelegate().getValue(); + const attribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); if (ensembleIdent && surfaceName && attribute && realizationNum !== null) { diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts index 9829608da..6d61463c6 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts @@ -3,9 +3,9 @@ import { getRealizationSurfacesMetadataOptions } from "@api"; import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; @@ -22,7 +22,7 @@ export class RealizationSurfaceSettingsContext implements SettingsContext(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.SURFACE_ATTRIBUTE]: new SurfaceAttributeSetting(), + [SettingType.ATTRIBUTE]: new AttributeSetting(), [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), }); @@ -83,7 +83,7 @@ export class RealizationSurfaceSettingsContext implements SettingsContext { + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { const data = getHelperDependency(realizationSurfaceMetadataDep); if (!data) { @@ -98,7 +98,7 @@ export class RealizationSurfaceSettingsContext implements SettingsContext { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const attribute = getLocalSetting(SettingType.ATTRIBUTE); const data = getHelperDependency(realizationSurfaceMetadataDep); if (!attribute || !data) { @@ -117,7 +117,7 @@ export class RealizationSurfaceSettingsContext implements SettingsContext { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const attribute = getLocalSetting(SettingType.ATTRIBUTE); const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); const data = getHelperDependency(realizationSurfaceMetadataDep); diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts index c0225d599..48ea63d5b 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts @@ -4,7 +4,7 @@ import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTy export type RealizationSurfaceSettings = { [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; [SettingType.REALIZATION]: number | null; - [SettingType.SURFACE_ATTRIBUTE]: string | null; + [SettingType.ATTRIBUTE]: string | null; [SettingType.SURFACE_NAME]: string | null; [SettingType.TIME_OR_INTERVAL]: string | null; }; diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts index 28541edef..33612dd53 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceLayer.ts @@ -79,7 +79,7 @@ export class StatisticalSurfaceLayer const settings = this.getSettingsContext().getDelegate().getSettings(); const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); - const attribute = settings[SettingType.SURFACE_ATTRIBUTE].getDelegate().getValue(); + const attribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); const statisticFunction = settings[SettingType.STATISTIC_FUNCTION].getDelegate().getValue(); const sensitivityNameCasePair = settings[SettingType.SENSITIVITY].getDelegate().getValue(); diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts index 604ae0c28..9a397eaf1 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts @@ -3,13 +3,13 @@ import { getRealizationSurfacesMetadataOptions } from "@api"; import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; import { SensitivityNameCasePair, SensitivitySetting, } from "@modules/_shared/LayerFramework/settings/implementations/SensitivitySetting"; import { StatisticFunctionSetting } from "@modules/_shared/LayerFramework/settings/implementations/StatisticFunctionSetting"; -import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; @@ -27,7 +27,7 @@ export class StatisticalSurfaceSettingsContext implements SettingsContext { + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { const data = getHelperDependency(surfaceMetadataDep); if (!data) { @@ -115,7 +115,7 @@ export class StatisticalSurfaceSettingsContext implements SettingsContext { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const attribute = getLocalSetting(SettingType.ATTRIBUTE); const data = getHelperDependency(surfaceMetadataDep); if (!attribute || !data) { @@ -134,7 +134,7 @@ export class StatisticalSurfaceSettingsContext implements SettingsContext { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const attribute = getLocalSetting(SettingType.ATTRIBUTE); const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); const data = getHelperDependency(surfaceMetadataDep); diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts index 9df5760de..96185304b 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/types.ts @@ -7,7 +7,7 @@ export type StatisticalSurfaceSettings = { [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; [SettingType.STATISTIC_FUNCTION]: SurfaceStatisticFunction_api; [SettingType.SENSITIVITY]: SensitivityNameCasePair | null; - [SettingType.SURFACE_ATTRIBUTE]: string | null; + [SettingType.ATTRIBUTE]: string | null; [SettingType.SURFACE_NAME]: string | null; [SettingType.TIME_OR_INTERVAL]: string | null; }; diff --git a/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx index 2a2935c1a..a8e4f67d2 100644 --- a/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx @@ -28,8 +28,8 @@ import { View } from "@modules/_shared/LayerFramework/framework/View/View"; import { Group, Item, instanceofGroup, instanceofLayer } from "@modules/_shared/LayerFramework/interfaces"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; +import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { Dropdown } from "@mui/base"; @@ -111,8 +111,8 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper case "surface-name": groupDelegate.prependChild(new SharedSetting(new SurfaceNameSetting(), props.layerManager)); return; - case "surface-attribute": - groupDelegate.prependChild(new SharedSetting(new SurfaceAttributeSetting(), props.layerManager)); + case "attribute": + groupDelegate.prependChild(new SharedSetting(new AttributeSetting(), props.layerManager)); return; case "Date": groupDelegate.prependChild(new SharedSetting(new TimeOrIntervalSetting(), props.layerManager)); @@ -310,9 +310,9 @@ const LAYER_ACTIONS: LayersActionGroup[] = [ label: "Surface Name", }, { - identifier: "surface-attribute", + identifier: "attribute", icon: , - label: "Surface Attribute", + label: "Attribute", }, { identifier: "Date", diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts index 19f22a56a..af3089c10 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -108,7 +108,7 @@ export class RealizationGridLayer const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); const gridName = settings[SettingType.GRID_NAME].getDelegate().getValue(); - const attribute = settings[SettingType.GRID_ATTRIBUTE].getDelegate().getValue(); + const attribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); if (timeOrInterval === "NO_TIME") { timeOrInterval = null; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts index eb83f35a6..eef22bb55 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts @@ -2,8 +2,8 @@ import { getGridModelsInfoOptions } from "@api"; import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { GridAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting"; import { GridLayerIRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting"; import { GridLayerJRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting"; import { GridLayerKRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting"; @@ -26,7 +26,7 @@ export class RealizationGridSettingsContext implements SettingsContext { + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getLocalSetting, getHelperDependency }) => { const gridName = getLocalSetting(SettingType.GRID_NAME); const data = getHelperDependency(realizationGridDataDep); @@ -194,7 +194,7 @@ export class RealizationGridSettingsContext implements SettingsContext { const gridName = getLocalSetting(SettingType.GRID_NAME); - const gridAttribute = getLocalSetting(SettingType.GRID_ATTRIBUTE); + const gridAttribute = getLocalSetting(SettingType.ATTRIBUTE); const data = getHelperDependency(realizationGridDataDep); if (!gridName || !gridAttribute || !data) { diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts index bf195a92d..da1eb103d 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts @@ -4,7 +4,7 @@ import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTy export type RealizationGridSettings = { [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; [SettingType.REALIZATION]: number | null; - [SettingType.GRID_ATTRIBUTE]: string | null; + [SettingType.ATTRIBUTE]: string | null; [SettingType.GRID_NAME]: string | null; [SettingType.GRID_LAYER_I_RANGE]: [number, number] | null; [SettingType.GRID_LAYER_J_RANGE]: [number, number] | null; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index 4c04bad4f..eaaee0f87 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -75,7 +75,7 @@ export class RealizationSeismicCrosslineLayer const settings = this.getSettingsContext().getDelegate().getSettings(); const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); - const seismicAttribute = settings[SettingType.SEISMIC_ATTRIBUTE].getDelegate().getValue(); + const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); const seismicCrosslineNumber = settings[SettingType.SEISMIC_CROSSLINE].getDelegate().getValue(); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts index 271061c0d..8a3596b38 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts @@ -1,17 +1,11 @@ -import { getGridModelsInfoOptions, getSeismicCubeMetaListOptions } from "@api"; +import { getSeismicCubeMetaListOptions } from "@api"; import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { GridAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting"; -import { GridLayerIRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting"; -import { GridLayerJRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting"; -import { GridLayerKRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting"; -import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SeismicAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting"; import { SeismicCrosslineSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting"; -import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; @@ -29,7 +23,7 @@ export class RealizationSeismicCrosslineSettingsContext >(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.SEISMIC_ATTRIBUTE]: new SeismicAttributeSetting(), + [SettingType.ATTRIBUTE]: new AttributeSetting(), [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), [SettingType.SEISMIC_CROSSLINE]: new SeismicCrosslineSetting(), }); @@ -39,7 +33,7 @@ export class RealizationSeismicCrosslineSettingsContext return ( settings[SettingType.ENSEMBLE] !== null && settings[SettingType.REALIZATION] !== null && - settings[SettingType.SEISMIC_ATTRIBUTE] !== null && + settings[SettingType.ATTRIBUTE] !== null && settings[SettingType.TIME_OR_INTERVAL] !== null ); } @@ -99,7 +93,7 @@ export class RealizationSeismicCrosslineSettingsContext }); }); - availableSettingsUpdater(SettingType.SEISMIC_ATTRIBUTE, ({ getHelperDependency }) => { + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { const data = getHelperDependency(realizationSeismicCrosslineDataDep); if (!data) { @@ -114,7 +108,7 @@ export class RealizationSeismicCrosslineSettingsContext }); availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); const data = getHelperDependency(realizationSeismicCrosslineDataDep); @@ -135,7 +129,7 @@ export class RealizationSeismicCrosslineSettingsContext return availableTimeOrIntervals; }); availableSettingsUpdater(SettingType.SEISMIC_CROSSLINE, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); const timeOrInterval = getLocalSetting(SettingType.TIME_OR_INTERVAL); const data = getHelperDependency(realizationSeismicCrosslineDataDep); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts index 774983ff4..3b04717ef 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts @@ -4,7 +4,7 @@ import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTy export type RealizationSeismicCrosslineSettings = { [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; [SettingType.REALIZATION]: number | null; - [SettingType.SEISMIC_ATTRIBUTE]: string | null; + [SettingType.ATTRIBUTE]: string | null; [SettingType.TIME_OR_INTERVAL]: string | null; [SettingType.SEISMIC_CROSSLINE]: number | null; }; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts index 0d4a4153a..c869292fb 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts @@ -80,7 +80,7 @@ export class RealizationSeismicDepthSliceLayer const settings = this.getSettingsContext().getDelegate().getSettings(); const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); - const seismicAttribute = settings[SettingType.SEISMIC_ATTRIBUTE].getDelegate().getValue(); + const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); const seismicDepth = settings[SettingType.SEISMIC_DEPTH_SLICE].getDelegate().getValue(); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts index 4590d384e..495169276 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts @@ -2,9 +2,9 @@ import { getSeismicCubeMetaListOptions } from "@api"; import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SeismicAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting"; import { SeismicDepthSliceSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicDepthSliceSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; @@ -23,7 +23,7 @@ export class RealizationSeismicDepthSliceSettingsContext >(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.SEISMIC_ATTRIBUTE]: new SeismicAttributeSetting(), + [SettingType.ATTRIBUTE]: new AttributeSetting(), [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), [SettingType.SEISMIC_DEPTH_SLICE]: new SeismicDepthSliceSetting(), }); @@ -33,7 +33,7 @@ export class RealizationSeismicDepthSliceSettingsContext return ( settings[SettingType.ENSEMBLE] !== null && settings[SettingType.REALIZATION] !== null && - settings[SettingType.SEISMIC_ATTRIBUTE] !== null && + settings[SettingType.ATTRIBUTE] !== null && settings[SettingType.TIME_OR_INTERVAL] !== null ); } @@ -93,7 +93,7 @@ export class RealizationSeismicDepthSliceSettingsContext }); }); - availableSettingsUpdater(SettingType.SEISMIC_ATTRIBUTE, ({ getHelperDependency }) => { + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { const data = getHelperDependency(RealizationSeismicDepthSliceDataDep); if (!data) { @@ -108,7 +108,7 @@ export class RealizationSeismicDepthSliceSettingsContext }); availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); const data = getHelperDependency(RealizationSeismicDepthSliceDataDep); @@ -129,7 +129,7 @@ export class RealizationSeismicDepthSliceSettingsContext return availableTimeOrIntervals; }); availableSettingsUpdater(SettingType.SEISMIC_DEPTH_SLICE, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); const timeOrInterval = getLocalSetting(SettingType.TIME_OR_INTERVAL); const data = getHelperDependency(RealizationSeismicDepthSliceDataDep); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts index c0eb9be86..e2437beaf 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts @@ -4,7 +4,7 @@ import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTy export type RealizationSeismicDepthSliceSettings = { [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; [SettingType.REALIZATION]: number | null; - [SettingType.SEISMIC_ATTRIBUTE]: string | null; + [SettingType.ATTRIBUTE]: string | null; [SettingType.TIME_OR_INTERVAL]: string | null; [SettingType.SEISMIC_DEPTH_SLICE]: number | null; }; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts index e0f9a9c8f..5c0af36d7 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts @@ -73,7 +73,7 @@ export class RealizationSeismicInlineLayer implements Layer(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.SEISMIC_ATTRIBUTE]: new SeismicAttributeSetting(), - + [SettingType.ATTRIBUTE]: new AttributeSetting(), [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), [SettingType.SEISMIC_INLINE]: new SeismicInlineSetting(), }); @@ -38,7 +31,7 @@ export class RealizationSeismicInlineSettingsContext implements SettingsContext< return ( settings[SettingType.ENSEMBLE] !== null && settings[SettingType.REALIZATION] !== null && - settings[SettingType.SEISMIC_ATTRIBUTE] !== null && + settings[SettingType.ATTRIBUTE] !== null && settings[SettingType.TIME_OR_INTERVAL] !== null ); } @@ -98,7 +91,7 @@ export class RealizationSeismicInlineSettingsContext implements SettingsContext< }); }); - availableSettingsUpdater(SettingType.SEISMIC_ATTRIBUTE, ({ getHelperDependency }) => { + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { const data = getHelperDependency(realizationSeismicInlineDataDep); if (!data) { @@ -113,7 +106,7 @@ export class RealizationSeismicInlineSettingsContext implements SettingsContext< }); availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); const data = getHelperDependency(realizationSeismicInlineDataDep); @@ -134,7 +127,7 @@ export class RealizationSeismicInlineSettingsContext implements SettingsContext< return availableTimeOrIntervals; }); availableSettingsUpdater(SettingType.SEISMIC_INLINE, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.SEISMIC_ATTRIBUTE); + const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); const timeOrInterval = getLocalSetting(SettingType.TIME_OR_INTERVAL); const data = getHelperDependency(realizationSeismicInlineDataDep); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts index e09557287..c03351f50 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts @@ -4,7 +4,7 @@ import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTy export type RealizationSeismicInlineSettings = { [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; [SettingType.REALIZATION]: number | null; - [SettingType.SEISMIC_ATTRIBUTE]: string | null; + [SettingType.ATTRIBUTE]: string | null; [SettingType.TIME_OR_INTERVAL]: string | null; [SettingType.SEISMIC_INLINE]: number | null; }; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts index da2c1f95c..0179b131b 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts @@ -81,7 +81,7 @@ export class RealizationSurfaceLayer const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); - const attribute = settings[SettingType.SURFACE_ATTRIBUTE].getDelegate().getValue(); + const attribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); if (ensembleIdent && surfaceName && attribute && realizationNum !== null) { diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts index 9829608da..6d61463c6 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts @@ -3,9 +3,9 @@ import { getRealizationSurfacesMetadataOptions } from "@api"; import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SurfaceAttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting"; import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; @@ -22,7 +22,7 @@ export class RealizationSurfaceSettingsContext implements SettingsContext(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.SURFACE_ATTRIBUTE]: new SurfaceAttributeSetting(), + [SettingType.ATTRIBUTE]: new AttributeSetting(), [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), }); @@ -83,7 +83,7 @@ export class RealizationSurfaceSettingsContext implements SettingsContext { + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { const data = getHelperDependency(realizationSurfaceMetadataDep); if (!data) { @@ -98,7 +98,7 @@ export class RealizationSurfaceSettingsContext implements SettingsContext { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const attribute = getLocalSetting(SettingType.ATTRIBUTE); const data = getHelperDependency(realizationSurfaceMetadataDep); if (!attribute || !data) { @@ -117,7 +117,7 @@ export class RealizationSurfaceSettingsContext implements SettingsContext { - const attribute = getLocalSetting(SettingType.SURFACE_ATTRIBUTE); + const attribute = getLocalSetting(SettingType.ATTRIBUTE); const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); const data = getHelperDependency(realizationSurfaceMetadataDep); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts index c0225d599..48ea63d5b 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts @@ -4,7 +4,7 @@ import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTy export type RealizationSurfaceSettings = { [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; [SettingType.REALIZATION]: number | null; - [SettingType.SURFACE_ATTRIBUTE]: string | null; + [SettingType.ATTRIBUTE]: string | null; [SettingType.SURFACE_NAME]: string | null; [SettingType.TIME_OR_INTERVAL]: string | null; }; diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index 9549e2de7..067e946aa 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -27,6 +27,7 @@ import { View } from "@modules/_shared/LayerFramework/framework/View/View"; import { Group, Item, instanceofGroup, instanceofLayer } from "@modules/_shared/LayerFramework/interfaces"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; +import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { Dropdown } from "@mui/base"; @@ -109,6 +110,9 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper case "realization": groupDelegate.prependChild(new SharedSetting(new RealizationSetting(), props.layerManager)); return; + case "attribute": + groupDelegate.prependChild(new SharedSetting(new AttributeSetting(), props.layerManager)); + return; case "Date": groupDelegate.prependChild(new SharedSetting(new TimeOrIntervalSetting(), props.layerManager)); return; @@ -304,6 +308,11 @@ const LAYER_ACTIONS: LayersActionGroup[] = [ icon: , label: "Realization", }, + { + identifier: "attribute", + icon: , + label: "Attribute", + }, { identifier: "Date", icon: , diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/AttributeSetting.tsx similarity index 88% rename from frontend/src/modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting.tsx rename to frontend/src/modules/_shared/LayerFramework/settings/implementations/AttributeSetting.tsx index d12897bc6..eba5e3312 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridAttributeSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/AttributeSetting.tsx @@ -9,7 +9,7 @@ import { SettingType } from "../settingsTypes"; type ValueType = string | null; -export class GridAttributeSetting implements Setting { +export class AttributeSetting implements Setting { private _delegate: SettingDelegate; constructor() { @@ -17,11 +17,11 @@ export class GridAttributeSetting implements Setting { } getType(): SettingType { - return SettingType.GRID_ATTRIBUTE; + return SettingType.ATTRIBUTE; } getLabel(): string { - return "Grid attribute"; + return "Attribute"; } getDelegate(): SettingDelegate { @@ -50,4 +50,4 @@ export class GridAttributeSetting implements Setting { } } -SettingRegistry.registerSetting(GridAttributeSetting); +SettingRegistry.registerSetting(AttributeSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting.tsx deleted file mode 100644 index 8bdbc7fe7..000000000 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicAttributeSetting.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from "react"; - -import { Dropdown, DropdownOption } from "@lib/components/Dropdown"; - -import { SettingDelegate } from "../../delegates/SettingDelegate"; -import { Setting, SettingComponentProps } from "../../interfaces"; -import { SettingRegistry } from "../SettingRegistry"; -import { SettingType } from "../settingsTypes"; - -type ValueType = string | null; - -export class SeismicAttributeSetting implements Setting { - private _delegate: SettingDelegate; - - constructor() { - this._delegate = new SettingDelegate(null, this); - } - - getType(): SettingType { - return SettingType.SEISMIC_ATTRIBUTE; - } - - getLabel(): string { - return "Seismic Attribute"; - } - - getDelegate(): SettingDelegate { - return this._delegate; - } - - makeComponent(): (props: SettingComponentProps) => React.ReactNode { - return function Ensemble(props: SettingComponentProps) { - const options: DropdownOption[] = props.availableValues.map((value) => { - return { - value: value.toString(), - label: value === null ? "None" : value.toString(), - }; - }); - - return ( - - ); - }; - } -} - -SettingRegistry.registerSetting(SeismicAttributeSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting.tsx deleted file mode 100644 index 8564c554e..000000000 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SurfaceAttributeSetting.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from "react"; - -import { Dropdown, DropdownOption } from "@lib/components/Dropdown"; - -import { SettingDelegate } from "../../delegates/SettingDelegate"; -import { Setting, SettingComponentProps } from "../../interfaces"; -import { SettingRegistry } from "../SettingRegistry"; -import { SettingType } from "../settingsTypes"; - -type ValueType = string | null; - -export class SurfaceAttributeSetting implements Setting { - private _delegate: SettingDelegate; - - constructor() { - this._delegate = new SettingDelegate(null, this); - } - - getType(): SettingType { - return SettingType.SURFACE_ATTRIBUTE; - } - - getLabel(): string { - return "Surface attribute"; - } - - getDelegate(): SettingDelegate { - return this._delegate; - } - - makeComponent(): (props: SettingComponentProps) => React.ReactNode { - return function Ensemble(props: SettingComponentProps) { - const options: DropdownOption[] = props.availableValues.map((value) => { - return { - value: value.toString(), - label: value === null ? "None" : value.toString(), - }; - }); - - return ( - - ); - }; - } -} - -SettingRegistry.registerSetting(SurfaceAttributeSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts index f747c9c42..19d9fd554 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts +++ b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts @@ -4,19 +4,17 @@ export enum SettingType { STATISTIC_FUNCTION = "statisticFunction", SENSITIVITY = "sensitivity", SURFACE_NAME = "surfaceName", - SURFACE_ATTRIBUTE = "surfaceAttribute", + ATTRIBUTE = "attribute", TIME_OR_INTERVAL = "timeOrInterval", POLYGONS_ATTRIBUTE = "polygonsAttribute", POLYGONS_NAME = "polygonsName", SMDA_WELLBORE_HEADERS = "smdaWellboreHeaders", GRID_NAME = "gridName", - GRID_ATTRIBUTE = "gridAttribute", GRID_LAYER_I_RANGE = "gridLayerIRange", GRID_LAYER_J_RANGE = "gridLayerJRange", GRID_LAYER_K_RANGE = "gridLayerKRange", GRID_LAYER_K = "gridLayerK", SHOW_GRID_LINES = "showGridLines", - SEISMIC_ATTRIBUTE = "seismicAttribute", SEISMIC_INLINE = "seismicInline", SEISMIC_CROSSLINE = "seismicCrossline", SEISMIC_DEPTH_SLICE = "seismicDepthSlice", From ab511942164298392cb2a0c79372647a08f18ccb Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 28 Jan 2025 13:49:14 +0100 Subject: [PATCH 24/97] wip --- .../layerManagerComponentWrapper.tsx | 2 +- .../view/components/LayersWrapper.tsx | 2 +- .../layerManagerComponentWrapper.tsx | 2 +- .../view/components/LayersWrapper.tsx | 2 +- .../deckGlLayers/EditablePolylineLayer.ts | 42 +++-- .../editablePolylinesHook.tsx | 26 +++- .../view/utils/DeckGlInstanceManager.ts | 145 ++++++++++++++++++ .../3DViewerNew/view/utils/PolylinesPlugin.ts | 93 +++++++++++ .../LayerFramework/delegates/GroupDelegate.ts | 2 +- .../LayerFramework/delegates/ItemDelegate.ts | 2 +- .../LayerFramework/delegates/LayerDelegate.ts | 2 +- .../delegates/SettingDelegate.ts | 3 +- .../delegates/SettingsContextDelegate.ts | 2 +- .../ColorScale/ColorScaleComponent.tsx | 2 +- .../DeltaSurface/DeltaSurfaceComponent.tsx | 2 +- .../framework/LayerManager/LayerManager.ts | 2 +- .../LayerManager/LayerManagerComponent.tsx | 2 +- .../SettingsGroup/SettingsGroupComponent.tsx | 2 +- .../SharedSetting/SharedSettingComponent.tsx | 2 +- .../framework/View/ViewComponent.tsx | 2 +- .../framework/utilityComponents/EditName.tsx | 2 +- .../utilityComponents/VisibilityToggle.tsx | 2 +- .../LayerFramework/layers/LayerComponent.tsx | 2 +- .../settings/SettingComponent.tsx | 2 +- .../PublishSubscribeDelegate.ts | 0 25 files changed, 307 insertions(+), 40 deletions(-) create mode 100644 frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts create mode 100644 frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts rename frontend/src/modules/_shared/{LayerFramework/delegates => utils}/PublishSubscribeDelegate.ts (100%) diff --git a/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx index a8e4f67d2..b35e9ae57 100644 --- a/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx @@ -17,7 +17,6 @@ import { PreferredViewLayout } from "@modules/2DViewer/types"; import { EnsembleSetting } from "@modules//_shared/LayerFramework/settings/implementations/EnsembleSetting"; import { LayersActionGroup } from "@modules/_shared/LayerFramework/LayersActions"; import { GroupDelegate, GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; -import { usePublishSubscribeTopicValue } from "@modules/_shared/LayerFramework/delegates/PublishSubscribeDelegate"; import { ColorScale } from "@modules/_shared/LayerFramework/framework/ColorScale/ColorScale"; import { DeltaSurface } from "@modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurface"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; @@ -32,6 +31,7 @@ import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/imple import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { Dropdown } from "@mui/base"; import { Check, diff --git a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx index 5c14031ca..843ba8d1b 100644 --- a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx @@ -8,11 +8,11 @@ import { useElementSize } from "@lib/hooks/useElementSize"; import { Rect2D, outerRectContainsInnerRect } from "@lib/utils/geometry"; import { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; -import { usePublishSubscribeTopicValue } from "@modules/_shared/LayerFramework/delegates/PublishSubscribeDelegate"; import { LayerManager, LayerManagerTopic } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { BoundingBox } from "@modules/_shared/LayerFramework/interfaces"; import { ColorLegendsContainer } from "@modules/_shared/components/ColorLegendsContainer"; import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; +import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { BoundingBox2D, ViewportType } from "@webviz/subsurface-viewer"; import { ViewsType } from "@webviz/subsurface-viewer/dist/SubsurfaceViewer"; diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index 067e946aa..71de7cd51 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -16,7 +16,6 @@ import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFra import { EnsembleSetting } from "@modules//_shared/LayerFramework/settings/implementations/EnsembleSetting"; import { LayersActionGroup } from "@modules/_shared/LayerFramework/LayersActions"; import { GroupDelegate, GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; -import { usePublishSubscribeTopicValue } from "@modules/_shared/LayerFramework/delegates/PublishSubscribeDelegate"; import { ColorScale } from "@modules/_shared/LayerFramework/framework/ColorScale/ColorScale"; import { DeltaSurface } from "@modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurface"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; @@ -30,6 +29,7 @@ import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layer import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { Dropdown } from "@mui/base"; import { Check, diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index e68ceffe7..0b1047bd2 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -8,11 +8,11 @@ import { useElementSize } from "@lib/hooks/useElementSize"; import { Rect3D, outerRectContainsInnerRect } from "@lib/utils/geometry"; import { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; -import { usePublishSubscribeTopicValue } from "@modules/_shared/LayerFramework/delegates/PublishSubscribeDelegate"; import { LayerManager, LayerManagerTopic } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { BoundingBox } from "@modules/_shared/LayerFramework/interfaces"; import { ColorLegendsContainer } from "@modules/_shared/components/ColorLegendsContainer"; import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; +import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { BoundingBox3D, ViewportType } from "@webviz/subsurface-viewer"; import { ViewsType } from "@webviz/subsurface-viewer/dist/SubsurfaceViewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts index 21fb73058..d7f371bb6 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts @@ -1,16 +1,24 @@ import { CompositeLayer, GetPickingInfoParams, Layer, PickingInfo } from "@deck.gl/core"; import { PathStyleExtension } from "@deck.gl/extensions"; -import { ColumnLayer, LineLayer, PathLayer, ScatterplotLayer } from "@deck.gl/layers"; +import { LineLayer, PathLayer, ScatterplotLayer } from "@deck.gl/layers"; import { AnimatedPathLayer } from "./AnimatedPathLayer"; import { Polyline } from "../types"; +export enum AllowHoveringOf { + NONE = "none", + LINES = "line", + POINTS = "point", + LINES_AND_POINTS = "lines-and-points", +} + export type EditablePolylineLayerProps = { id: string; polyline: Polyline; mouseHoverPoint?: number[]; referencePathPointIndex?: number; + allowHoveringOf: AllowHoveringOf; }; export type EditablePolylineLayerPickingInfo = PickingInfo & { @@ -70,13 +78,21 @@ export class EditablePolylineLayer extends CompositeLayer d, getFillColor: [230, 136, 21, 255], getLineColor: [230, 136, 21, 255], getLineWidth: 10, - extruded: false, - radius: 30, - lineWidthMinPixels: 5, + getRadius: 10, + lineWidthMinPixels: 3, lineWidthMaxPixels: 5, radiusUnits: "pixels", lineWidthUnits: "pixels", @@ -180,9 +194,9 @@ export class EditablePolylineLayer extends CompositeLayer d, getWidth: 50, - widthMinPixels: 6, - widthMaxPixels: 10, - billboard: true, + widthMinPixels: 10, + widthMaxPixels: 20, + billboard: false, widthUnits: "meters", parameters: { depthTest: false, @@ -226,13 +240,13 @@ export class EditablePolylineLayer extends CompositeLayer d, - getFillColor: (d, context) => { + getFillColor: (_, context) => { if (context.index === referencePathPointIndex) { return [255, 255, 255, 255]; } return [230, 136, 21, 255]; }, - getLineColor: (d, context) => { + getLineColor: (_, context) => { if ( this.state.hoveredEntity && this.state.hoveredEntity.layer === "point" && @@ -242,7 +256,7 @@ export class EditablePolylineLayer extends CompositeLayer { + getLineWidth: (_, context) => { if ( this.state.hoveredEntity && this.state.hoveredEntity.layer === "point" && @@ -252,7 +266,7 @@ export class EditablePolylineLayer extends CompositeLayer { + getRadius: (_, context) => { if ( this.state.hoveredEntity?.layer === "point" && context.index === this.state.hoveredEntity.index diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx index d36a7e3a7..fe3ba5256 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx @@ -12,7 +12,11 @@ import { MapMouseEvent } from "@webviz/subsurface-viewer"; import { isEqual } from "lodash"; import { v4 } from "uuid"; -import { EditablePolylineLayer, isEditablePolylineLayerPickingInfo } from "./deckGlLayers/EditablePolylineLayer"; +import { + AllowHoveringOf, + EditablePolylineLayer, + isEditablePolylineLayerPickingInfo, +} from "./deckGlLayers/EditablePolylineLayer"; import { PolylinesLayer, isPolylinesLayerPickingInfo } from "./deckGlLayers/PolylinesLayer"; import { ContextMenuItem, Polyline, PolylineEditingMode } from "./types"; @@ -483,6 +487,17 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita }), ]; + let allowHoveringOf = AllowHoveringOf.NONE; + if (props.editingMode === PolylineEditingMode.DRAW) { + allowHoveringOf = AllowHoveringOf.LINES_AND_POINTS; + } + if (props.editingMode === PolylineEditingMode.ADD_POINT) { + allowHoveringOf = AllowHoveringOf.LINES; + } + if (props.editingMode === PolylineEditingMode.REMOVE_POINT) { + allowHoveringOf = AllowHoveringOf.POINTS; + } + if (activePolylineId) { const activePolyline = polylines.find((polyline) => polyline.id === activePolylineId); layers.push( @@ -494,6 +509,7 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita props.editingMode === PolylineEditingMode.DRAW ? referencePathPointIndex : undefined, onDragStart, onDragEnd, + allowHoveringOf, }) ); } @@ -509,22 +525,22 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita } if (cursorIcon === CursorIcon.CONTINUE_FROM_POINT) { - return `url(${continuePathIcon}), crosshair`; + return `url(${continuePathIcon}) 4 2, crosshair`; } if (cursorIcon === CursorIcon.ADD_POINT) { - return `url(${addPathIcon}), crosshair`; + return `url(${addPathIcon}) 4 2, crosshair`; } if (cursorIcon === CursorIcon.REMOVE_POINT) { - return `url(${removePathIcon}), crosshair`; + return `url(${removePathIcon}) 4 2, crosshair`; } if ( (activePolylineId && referencePathPointIndex !== undefined) || (!activePolylineId && props.editingMode === PolylineEditingMode.DRAW) ) { - return `url(${setPathPointIcon}), crosshair`; + return `url(${setPathPointIcon}) 4 2, crosshair`; } return "default"; diff --git a/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts b/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts new file mode 100644 index 000000000..165101b5c --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts @@ -0,0 +1,145 @@ +/* +This manager is responsible for managing plugins for DeckGL, forwarding events to them, and adding/adjusting layers based on the plugins' responses. +*/ +import { Layer, PickingInfo } from "@deck.gl/core"; +import { DeckGLRef } from "@deck.gl/react"; +import { + PublishSubscribe, + PublishSubscribeDelegate, + TopicPayloads, +} from "@modules_shared/utils/PublishSubscribeDelegate"; +import { MapMouseEvent, SubsurfaceViewerProps } from "@webviz/subsurface-viewer"; + +export enum DeckGlPluginTopic { + REQUIRE_REDRAW = "REQUIRE_REDRAW", +} + +export type DeckGlPluginPayloads = { + [DeckGlPluginTopic.REQUIRE_REDRAW]: undefined; +}; + +export class DeckGlPlugin< + TAdditionalTopic extends string | never = never, + TAdditionalPayloads extends TopicPayloads | never = never +> implements PublishSubscribe +{ + private _publishSubscribeDelegate = new PublishSubscribeDelegate(); + + private requireRedraw() { + this._publishSubscribeDelegate.notifySubscribers(DeckGlPluginTopic.REQUIRE_REDRAW); + } + + handleDragStart?(pickingInfo: PickingInfo): void; + handleDrag?(pickingInfo: PickingInfo, deckGlRef: DeckGLRef): void; + handleMouseEvent?(event: MapMouseEvent): void; + handleKeyUpEvent?(key: string): void; + handleKeyDownEvent?(key: string): void; + getCursor?(): string | null; + + protected makeSnapshot?( + topic: T, + ): TAdditionalPayloads[T]; + + makeSnapshotGetter( + topic: T + ): () => (DeckGlPluginPayloads & TAdditionalPayloads)[T] { + const snapshotGetter = (): any => { + if (topic === DeckGlPluginTopic.REQUIRE_REDRAW) { + return undefined; + } + }; + + return snapshotGetter; + } + + getPublishSubscribeDelegate(): PublishSubscribeDelegate { + return this._publishSubscribeDelegate; + } +} + +export enum DeckGlInstanceManagerTopic { + REDRAW = "REDRAW", +} + +export type DeckGlInstanceManagerPayloads = { + [DeckGlInstanceManagerTopic.REDRAW]: undefined; +}; + +export class DeckGlInstanceManager implements PublishSubscribe { + private _publishSubscribeDelegate = new PublishSubscribeDelegate(); + + private _ref: DeckGLRef; + private _hoverPoint: [number, number, number] | null = null; + private _plugins: DeckGlPlugin[] = []; + private _layers: Layer[] = []; + private _layersIdPluginMap = new Map(); + + constructor(ref: DeckGLRef) { + this._ref = ref; + } + + addPlugin(plugin: DeckGlPlugin) { + this._plugins.push(plugin); + } + + addLayers(layers: Layer) { + this._layers.push(layers); + } + + redraw() {} + + getPublishSubscribeDelegate(): PublishSubscribeDelegate { + return this._publishSubscribeDelegate; + } + + makeSnapshotGetter(topic: T): () => DeckGlPluginPayloads[T] { + const snapshotGetter = (): any => { + if (topic === DeckGlPluginTopic.REQUIRE_REDRAW) { + return undefined; + } + }; + + return snapshotGetter; + } + + private getLayerIdFromPickingInfo(pickingInfo: PickingInfo): string | undefined { + return pickingInfo.layer?.id; + } + + handleDrag(pickingInfo: PickingInfo): void { + const layerId = this.getLayerIdFromPickingInfo(pickingInfo); + if (!layerId) { + return; + } + + const plugin = this._layersIdPluginMap.get(layerId); + if (!plugin) { + return; + } + + plugin.handleDrag?.(pickingInfo, this._ref); + } + + handleDragStart(pickingInfo: PickingInfo) { + const layerId = this.getLayerIdFromPickingInfo(pickingInfo); + if (!layerId) { + return; + } + + const plugin = this._layersIdPluginMap.get(layerId); + if (!plugin) { + return; + } + + plugin.handleDragStart?.(pickingInfo); + } + + handleMouseEvent(event: MapMouseEvent) {} + + makeDeckGlComponentProps(): Partial { + return { + onDrag: this.handleDrag.bind(this), + onMouseEvent: this.handleMouseEvent.bind(this), + }; + } +} diff --git a/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts new file mode 100644 index 000000000..b452ec416 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts @@ -0,0 +1,93 @@ +import { MapMouseEvent } from "@webviz/subsurface-viewer"; + +import { DeckGlPlugin } from "./DeckGlInstanceManager"; + +export type Polyline = { + id: string; + name: string; + color: [number, number, number, number]; + path: [number, number, number][]; +}; + +export enum PolylineEditingMode { + DRAW = "draw", + ADD_POINT = "add_point", + REMOVE_POINT = "remove_point", + NONE = "none", + IDLE = "idle", +} + +export enum PolylinesPluginTopic { + EDITING_POLYLINE_CHANGE = "editing_polyline_change", + EDITING_MODE_CHANGE = "editing_mode_change", +} + +export type PolylinesPluginTopicPayloads = { + [PolylinesPluginTopic.EDITING_MODE_CHANGE]: { + editingMode: PolylineEditingMode; + }; + [PolylinesPluginTopic.EDITING_POLYLINE_CHANGE]: { + editingPolylineId: string | null; + }; +}; + +enum AppendToPathLocation { + START = "start", + END = "end", +} + +export class PolylinesPlugin extends DeckGlPlugin { + private _currentEditingPolylineId: string | null = null; + private _currentEditingPolylinePathReferencePointIndex: number | null = null; + private _polylines: Polyline[] = []; + private _editingMode: PolylineEditingMode = PolylineEditingMode.NONE; + private _draggedPointIndex: number | null = null; + private _appendToPathLocation: AppendToPathLocation = AppendToPathLocation.END; + + private setCurrentEditingPolylineId(id: string | null): void { + this._currentEditingPolylineId = id; + super.getPublishSubscribeDelegate().notifySubscribers(PolylinesPluginTopic.EDITING_POLYLINE_CHANGE); + } + + handleKeyUpEvent(key: string): void { + if (key === "Escape") { + if (this._editingMode === PolylineEditingMode.NONE) { + this.setCurrentEditingPolylineId(null); + this._currentEditingPolylinePathReferencePointIndex = null; + } + if (this._editingMode == PolylineEditingMode.IDLE) { + this._currentEditingPolylinePathReferencePointIndex = null; + } + + this._editingMode = PolylineEditingMode.IDLE; + } + } + + handleMouseEvent(event: MapMouseEvent): void { + if (this._editingMode === PolylineEditingMode.NONE) { + return; + } + + if (event.type === "click") { + if (this._editingMode === PolylineEditingMode.DRAW) { + this.setCurrentEditingPolylineId(null); + this._editingMode = PolylineEditingMode.NONE; + } + } + } + + protected makeSnapshot(topic: T): PolylinesPluginTopicPayloads[T] { + if (topic === PolylinesPluginTopic.EDITING_MODE_CHANGE) { + return { + editingMode: this._editingMode, + } as PolylinesPluginTopicPayloads[T]; + } + if (topic === PolylinesPluginTopic.EDITING_POLYLINE_CHANGE) { + return { + editingPolylineId: this._currentEditingPolylineId, + } as PolylinesPluginTopicPayloads[T]; + } + + throw new Error("Unknown topic"); + } +} diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/GroupDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/GroupDelegate.ts index 3be0e82ef..8580f6750 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/GroupDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/GroupDelegate.ts @@ -1,7 +1,7 @@ import { ItemDelegateTopic } from "./ItemDelegate"; -import { PublishSubscribe, PublishSubscribeDelegate } from "./PublishSubscribeDelegate"; import { UnsubscribeHandlerDelegate } from "./UnsubscribeHandlerDelegate"; +import { PublishSubscribe, PublishSubscribeDelegate } from "../../utils/PublishSubscribeDelegate"; import { LayerManagerTopic } from "../framework/LayerManager/LayerManager"; import { SharedSetting } from "../framework/SharedSetting/SharedSetting"; import { DeserializationFactory } from "../framework/utils/DeserializationFactory"; diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/ItemDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/ItemDelegate.ts index 607a70195..95ea39f3d 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/ItemDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/ItemDelegate.ts @@ -2,8 +2,8 @@ import { isEqual } from "lodash"; import { v4 } from "uuid"; import { GroupDelegate } from "./GroupDelegate"; -import { PublishSubscribe, PublishSubscribeDelegate } from "./PublishSubscribeDelegate"; +import { PublishSubscribe, PublishSubscribeDelegate } from "../../utils/PublishSubscribeDelegate"; import { LayerManager, LayerManagerTopic } from "../framework/LayerManager/LayerManager"; import { SerializedItem } from "../interfaces"; diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts index 2ba7c353a..a36fcce71 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts @@ -3,10 +3,10 @@ import { ApiErrorHelper } from "@framework/utils/ApiErrorHelper"; import { isDevMode } from "@lib/utils/devMode"; import { QueryClient, isCancelledError } from "@tanstack/react-query"; -import { PublishSubscribe, PublishSubscribeDelegate } from "./PublishSubscribeDelegate"; import { SettingsContextDelegateTopic } from "./SettingsContextDelegate"; import { UnsubscribeHandlerDelegate } from "./UnsubscribeHandlerDelegate"; +import { PublishSubscribe, PublishSubscribeDelegate } from "../../utils/PublishSubscribeDelegate"; import { LayerManager, LayerManagerTopic } from "../framework/LayerManager/LayerManager"; import { SharedSetting } from "../framework/SharedSetting/SharedSetting"; import { BoundingBox, Layer, SerializedLayer, SerializedType, Settings, SettingsContext } from "../interfaces"; diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts index a6048dffc..93127f5da 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts @@ -4,8 +4,7 @@ import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { isArray, isEqual } from "lodash"; import { v4 } from "uuid"; -import { PublishSubscribe, PublishSubscribeDelegate } from "./PublishSubscribeDelegate"; - +import { PublishSubscribe, PublishSubscribeDelegate } from "../../utils/PublishSubscribeDelegate"; import { AvailableValuesType, Setting } from "../interfaces"; export enum SettingTopic { diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts index 402bd7139..64210ea68 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts @@ -1,8 +1,8 @@ -import { PublishSubscribe, PublishSubscribeDelegate } from "./PublishSubscribeDelegate"; import { SettingTopic } from "./SettingDelegate"; import { UnsubscribeHandlerDelegate } from "./UnsubscribeHandlerDelegate"; import { Dependency } from "./_utils/Dependency"; +import { PublishSubscribe, PublishSubscribeDelegate } from "../../utils/PublishSubscribeDelegate"; import { GlobalSettings, LayerManager, LayerManagerTopic } from "../framework/LayerManager/LayerManager"; import { AvailableValuesType, diff --git a/frontend/src/modules/_shared/LayerFramework/framework/ColorScale/ColorScaleComponent.tsx b/frontend/src/modules/_shared/LayerFramework/framework/ColorScale/ColorScaleComponent.tsx index 2e7529a2f..5c30428a4 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/ColorScale/ColorScaleComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/framework/ColorScale/ColorScaleComponent.tsx @@ -11,8 +11,8 @@ import { ExpandLess, ExpandMore } from "@mui/icons-material"; import { ColorScale } from "./ColorScale"; +import { usePublishSubscribeTopicValue } from "../../../utils/PublishSubscribeDelegate"; import { ItemDelegateTopic } from "../../delegates/ItemDelegate"; -import { usePublishSubscribeTopicValue } from "../../delegates/PublishSubscribeDelegate"; import { RemoveItemButton } from "../utilityComponents/RemoveItemButton"; export type ColorScaleComponentProps = { diff --git a/frontend/src/modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurfaceComponent.tsx b/frontend/src/modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurfaceComponent.tsx index 47fcbbfc7..e5a127f9e 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurfaceComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurfaceComponent.tsx @@ -2,10 +2,10 @@ import { SortableListGroup } from "@lib/components/SortableList"; import { DeltaSurface } from "./DeltaSurface"; +import { usePublishSubscribeTopicValue } from "../../../utils/PublishSubscribeDelegate"; import { LayersActionGroup, LayersActions } from "../../LayersActions"; import { GroupDelegateTopic } from "../../delegates/GroupDelegate"; import { ItemDelegateTopic } from "../../delegates/ItemDelegate"; -import { usePublishSubscribeTopicValue } from "../../delegates/PublishSubscribeDelegate"; import { Group, Item, instanceofLayer } from "../../interfaces"; import { EditName } from "../utilityComponents/EditName"; import { EmptyContent } from "../utilityComponents/EmptyContent"; diff --git a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts index e4aaed2a9..5935bb46f 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts +++ b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts @@ -10,9 +10,9 @@ import { QueryClient } from "@tanstack/react-query"; import { isEqual } from "lodash"; +import { PublishSubscribe, PublishSubscribeDelegate } from "../../../utils/PublishSubscribeDelegate"; import { GroupDelegate, GroupDelegateTopic } from "../../delegates/GroupDelegate"; import { ItemDelegate } from "../../delegates/ItemDelegate"; -import { PublishSubscribe, PublishSubscribeDelegate } from "../../delegates/PublishSubscribeDelegate"; import { UnsubscribeHandlerDelegate } from "../../delegates/UnsubscribeHandlerDelegate"; import { Group, Item, SerializedLayerManager, SerializedType } from "../../interfaces"; diff --git a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManagerComponent.tsx b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManagerComponent.tsx index 0ae25ef2c..e652032f2 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManagerComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManagerComponent.tsx @@ -4,8 +4,8 @@ import { IsMoveAllowedArgs, SortableList } from "@lib/components/SortableList"; import { useElementSize } from "@lib/hooks/useElementSize"; import { convertRemToPixels } from "@lib/utils/screenUnitConversions"; import { GroupDelegate, GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; -import { usePublishSubscribeTopicValue } from "@modules/_shared/LayerFramework/delegates/PublishSubscribeDelegate"; import { Group, Item, instanceofGroup } from "@modules/_shared/LayerFramework/interfaces"; +import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { Add } from "@mui/icons-material"; import { LayerManager } from "./LayerManager"; diff --git a/frontend/src/modules/_shared/LayerFramework/framework/SettingsGroup/SettingsGroupComponent.tsx b/frontend/src/modules/_shared/LayerFramework/framework/SettingsGroup/SettingsGroupComponent.tsx index e121fb3f3..7905997dd 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/SettingsGroup/SettingsGroupComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/framework/SettingsGroup/SettingsGroupComponent.tsx @@ -1,10 +1,10 @@ import { SortableListGroup } from "@lib/components/SortableList"; import { SettingsApplications } from "@mui/icons-material"; +import { usePublishSubscribeTopicValue } from "../../../utils/PublishSubscribeDelegate"; import { LayersActionGroup, LayersActions } from "../../LayersActions"; import { GroupDelegateTopic } from "../../delegates/GroupDelegate"; import { ItemDelegateTopic } from "../../delegates/ItemDelegate"; -import { usePublishSubscribeTopicValue } from "../../delegates/PublishSubscribeDelegate"; import { Group, Item } from "../../interfaces"; import { EmptyContent } from "../utilityComponents/EmptyContent"; import { ExpandCollapseAllButton } from "../utilityComponents/ExpandCollapseAllButton"; diff --git a/frontend/src/modules/_shared/LayerFramework/framework/SharedSetting/SharedSettingComponent.tsx b/frontend/src/modules/_shared/LayerFramework/framework/SharedSetting/SharedSettingComponent.tsx index 0ad9c38af..609601b3b 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/SharedSetting/SharedSettingComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/framework/SharedSetting/SharedSettingComponent.tsx @@ -8,8 +8,8 @@ import { Delete, ExpandLess, ExpandMore, Link } from "@mui/icons-material"; import { SharedSetting } from "./SharedSetting"; +import { usePublishSubscribeTopicValue } from "../../../utils/PublishSubscribeDelegate"; import { ItemDelegateTopic } from "../../delegates/ItemDelegate"; -import { usePublishSubscribeTopicValue } from "../../delegates/PublishSubscribeDelegate"; import { SettingComponent } from "../../settings/SettingComponent"; export type SharedSettingComponentProps = { diff --git a/frontend/src/modules/_shared/LayerFramework/framework/View/ViewComponent.tsx b/frontend/src/modules/_shared/LayerFramework/framework/View/ViewComponent.tsx index 86817a450..fb5c8d5c0 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/View/ViewComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/framework/View/ViewComponent.tsx @@ -1,9 +1,9 @@ import { SortableListGroup } from "@lib/components/SortableList"; +import { usePublishSubscribeTopicValue } from "../../../utils/PublishSubscribeDelegate"; import { LayersActionGroup, LayersActions } from "../../LayersActions"; import { GroupDelegateTopic } from "../../delegates/GroupDelegate"; import { ItemDelegateTopic } from "../../delegates/ItemDelegate"; -import { usePublishSubscribeTopicValue } from "../../delegates/PublishSubscribeDelegate"; import { Group, Item } from "../../interfaces"; import { EditName } from "../utilityComponents/EditName"; import { EmptyContent } from "../utilityComponents/EmptyContent"; diff --git a/frontend/src/modules/_shared/LayerFramework/framework/utilityComponents/EditName.tsx b/frontend/src/modules/_shared/LayerFramework/framework/utilityComponents/EditName.tsx index 4e1a418e3..a2eed0bb7 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/utilityComponents/EditName.tsx +++ b/frontend/src/modules/_shared/LayerFramework/framework/utilityComponents/EditName.tsx @@ -2,8 +2,8 @@ import React from "react"; import { Edit } from "@mui/icons-material"; +import { usePublishSubscribeTopicValue } from "../../../utils/PublishSubscribeDelegate"; import { ItemDelegateTopic } from "../../delegates/ItemDelegate"; -import { usePublishSubscribeTopicValue } from "../../delegates/PublishSubscribeDelegate"; import { Item } from "../../interfaces"; type EditItemNameProps = { diff --git a/frontend/src/modules/_shared/LayerFramework/framework/utilityComponents/VisibilityToggle.tsx b/frontend/src/modules/_shared/LayerFramework/framework/utilityComponents/VisibilityToggle.tsx index 0f976b0be..f9d0a4450 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/utilityComponents/VisibilityToggle.tsx +++ b/frontend/src/modules/_shared/LayerFramework/framework/utilityComponents/VisibilityToggle.tsx @@ -1,8 +1,8 @@ import { DenseIconButton } from "@lib/components/DenseIconButton"; import { Visibility, VisibilityOff } from "@mui/icons-material"; +import { usePublishSubscribeTopicValue } from "../../../utils/PublishSubscribeDelegate"; import { ItemDelegateTopic } from "../../delegates/ItemDelegate"; -import { usePublishSubscribeTopicValue } from "../../delegates/PublishSubscribeDelegate"; import { Item } from "../../interfaces"; export type VisibilityToggleProps = { diff --git a/frontend/src/modules/_shared/LayerFramework/layers/LayerComponent.tsx b/frontend/src/modules/_shared/LayerFramework/layers/LayerComponent.tsx index db3ef4490..e88868c63 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/LayerComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/layers/LayerComponent.tsx @@ -7,9 +7,9 @@ import { SortableListItem } from "@lib/components/SortableList"; import { resolveClassNames } from "@lib/utils/resolveClassNames"; import { Block, CheckCircle, Difference, Error, ExpandLess, ExpandMore } from "@mui/icons-material"; +import { usePublishSubscribeTopicValue } from "../../utils/PublishSubscribeDelegate"; import { ItemDelegateTopic } from "../delegates/ItemDelegate"; import { LayerDelegateTopic, LayerStatus } from "../delegates/LayerDelegate"; -import { usePublishSubscribeTopicValue } from "../delegates/PublishSubscribeDelegate"; import { SettingsContextDelegateTopic, SettingsContextLoadingState } from "../delegates/SettingsContextDelegate"; import { EditName } from "../framework/utilityComponents/EditName"; import { RemoveItemButton } from "../framework/utilityComponents/RemoveItemButton"; diff --git a/frontend/src/modules/_shared/LayerFramework/settings/SettingComponent.tsx b/frontend/src/modules/_shared/LayerFramework/settings/SettingComponent.tsx index 360bc91c2..35def617a 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/SettingComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/SettingComponent.tsx @@ -4,7 +4,7 @@ import { PendingWrapper } from "@lib/components/PendingWrapper"; import { resolveClassNames } from "@lib/utils/resolveClassNames"; import { Link, Warning } from "@mui/icons-material"; -import { usePublishSubscribeTopicValue } from "../delegates/PublishSubscribeDelegate"; +import { usePublishSubscribeTopicValue } from "../../utils/PublishSubscribeDelegate"; import { SettingTopic } from "../delegates/SettingDelegate"; import { LayerManager, LayerManagerTopic } from "../framework/LayerManager/LayerManager"; import { Setting, SettingComponentProps as SettingComponentPropsInterface } from "../interfaces"; diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/PublishSubscribeDelegate.ts b/frontend/src/modules/_shared/utils/PublishSubscribeDelegate.ts similarity index 100% rename from frontend/src/modules/_shared/LayerFramework/delegates/PublishSubscribeDelegate.ts rename to frontend/src/modules/_shared/utils/PublishSubscribeDelegate.ts From eb5f8c6739ba63ca06315fcab936c145a484cb18 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 28 Jan 2025 17:06:19 +0100 Subject: [PATCH 25/97] wip --- .../view/components/ReadoutWrapper.tsx | 42 +++-- .../editablePolylinesHook.tsx | 4 - .../view/utils/DeckGlInstanceManager.ts | 150 ++++++++++++------ .../3DViewerNew/view/utils/PolylinesPlugin.ts | 73 ++++++++- 4 files changed, 189 insertions(+), 80 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index acfb6e369..cbc5b89b0 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -2,19 +2,16 @@ import React from "react"; import { Layer as DeckGlLayer, PickingInfo } from "@deck.gl/core"; import { DeckGLRef } from "@deck.gl/react"; -import { Menu } from "@lib/components/Menu"; -import { MenuItem } from "@lib/components/MenuItem"; import { SubsurfaceViewerWithCameraState } from "@modules/_shared/components/SubsurfaceViewerWithCameraState"; import { BoundingBox3D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; -import { isEqual } from "lodash"; - import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; import { Toolbar } from "./Toolbar"; -import { useEditablePolylines } from "../hooks/editablePolylines/editablePolylinesHook"; import { Polyline, PolylineEditingMode } from "../hooks/editablePolylines/types"; +import { DeckGlInstanceManager } from "../utils/DeckGlInstanceManager"; +import { PolylinesPlugin } from "../utils/PolylinesPlugin"; export type ReadooutWrapperProps = { views: ViewsType; @@ -26,6 +23,18 @@ export type ReadooutWrapperProps = { export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const id = React.useId(); const deckGlRef = React.useRef(null); + const [deckGlManager, setDeckGlManager] = React.useState( + new DeckGlInstanceManager(deckGlRef.current) + ); + const [polylinesPlugin, setPolylinesPlugin] = React.useState(new PolylinesPlugin(deckGlManager)); + + React.useEffect(function setupDeckGlManager() { + const manager = new DeckGlInstanceManager(deckGlRef.current); + setDeckGlManager(manager); + + const polylinesPlugin = new PolylinesPlugin(manager); + manager.addPlugin(polylinesPlugin); + }, []); const [cameraPositionSetByAction, setCameraPositionSetByAction] = React.useState(null); const [triggerHomeCounter, setTriggerHomeCounter] = React.useState(0); @@ -35,6 +44,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const [polylineEditingMode, setPolylineEditingMode] = React.useState(PolylineEditingMode.NONE); const [polylines, setPolylines] = React.useState([]); + /* const { onMouseEvent, layers, @@ -54,6 +64,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { if (!isEqual(changedPolylines, polylines)) { setPolylines(changedPolylines); } + */ function handleFitInViewClick() { setTriggerHomeCounter((prev) => prev + 1); @@ -64,7 +75,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { } function handlePolylineEditingModeChange(mode: PolylineEditingMode) { - setPolylineEditingMode(mode); + polylinesPlugin.setEditingMode(mode); } function handleMouseHover(event: MapMouseEvent): void { @@ -76,7 +87,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { handleMouseHover(event); } - onMouseEvent(event); + // onMouseEvent(event); } function handleVerticalScaleChange(value: number) { @@ -84,9 +95,11 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { } function handleDrag(info: PickingInfo): void { - onDrag(info); + // onDrag(info); } + const activePolylineId = polylinesPlugin.getCurrentEditingPolylineId(); + const handlePolylineNameChange = React.useCallback( function handlePolylineNameChange(name: string): void { if (!activePolylineId) { @@ -114,7 +127,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); } - adjustedLayers.push(...layers); + // adjustedLayers.push(...layers); return ( <> @@ -124,12 +137,12 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { onPolylineEditingModeChange={handlePolylineEditingModeChange} onVerticalScaleChange={handleVerticalScaleChange} verticalScale={verticalScale} - hasActivePolyline={Boolean(activePolylineId)} + hasActivePolyline={Boolean()} polylineEditingMode={polylineEditingMode} onPolylineNameChange={handlePolylineNameChange} activePolylineName={polylines.find((p) => p.id === activePolylineId)?.name} /> - {cursorPosition && contextMenuItems.length && ( + {/*cursorPosition && contextMenuItems.length && ( {contextMenuItems.map((item, index) => ( @@ -138,7 +151,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { ))} - )} + )*/} setCameraPositionSetByAction(null)} - onDrag={handleDrag} - onMouseEvent={handleMouseEvent} - layers={adjustedLayers} verticalScale={verticalScale} scale={{ visible: true, @@ -166,7 +176,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { }} triggerHome={triggerHomeCounter} pickingRadius={5} - getCursor={getCursor} + {...deckGlManager.makeDeckGlComponentProps(adjustedLayers)} > {props.viewportAnnotations} diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx index fe3ba5256..894098eca 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx @@ -422,10 +422,6 @@ export function useEditablePolylines(props: UseEditablePolylinesProps): UseEdita return false; } - if (!info.viewport) { - return false; - } - if (!props.deckGlRef.current) { return false; } diff --git a/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts b/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts index 165101b5c..f4d713259 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts @@ -2,12 +2,8 @@ This manager is responsible for managing plugins for DeckGL, forwarding events to them, and adding/adjusting layers based on the plugins' responses. */ import { Layer, PickingInfo } from "@deck.gl/core"; -import { DeckGLRef } from "@deck.gl/react"; -import { - PublishSubscribe, - PublishSubscribeDelegate, - TopicPayloads, -} from "@modules_shared/utils/PublishSubscribeDelegate"; +import { DeckGLProps, DeckGLRef } from "@deck.gl/react"; +import { PublishSubscribe, PublishSubscribeDelegate } from "@modules_shared/utils/PublishSubscribeDelegate"; import { MapMouseEvent, SubsurfaceViewerProps } from "@webviz/subsurface-viewer"; export enum DeckGlPluginTopic { @@ -18,43 +14,25 @@ export type DeckGlPluginPayloads = { [DeckGlPluginTopic.REQUIRE_REDRAW]: undefined; }; -export class DeckGlPlugin< - TAdditionalTopic extends string | never = never, - TAdditionalPayloads extends TopicPayloads | never = never -> implements PublishSubscribe -{ - private _publishSubscribeDelegate = new PublishSubscribeDelegate(); +export class DeckGlPlugin { + private _manager: DeckGlInstanceManager; + + constructor(manager: DeckGlInstanceManager) { + this._manager = manager; + } private requireRedraw() { - this._publishSubscribeDelegate.notifySubscribers(DeckGlPluginTopic.REQUIRE_REDRAW); + this._manager.redraw(); } handleDragStart?(pickingInfo: PickingInfo): void; - handleDrag?(pickingInfo: PickingInfo, deckGlRef: DeckGLRef): void; - handleMouseEvent?(event: MapMouseEvent): void; + handleDrag?(pickingInfo: PickingInfo): void; + handleMouseHover?(pickingInfo: PickingInfo): void; + handleMouseClick?(pickingInfo: PickingInfo): void; handleKeyUpEvent?(key: string): void; handleKeyDownEvent?(key: string): void; - getCursor?(): string | null; - - protected makeSnapshot?( - topic: T, - ): TAdditionalPayloads[T]; - - makeSnapshotGetter( - topic: T - ): () => (DeckGlPluginPayloads & TAdditionalPayloads)[T] { - const snapshotGetter = (): any => { - if (topic === DeckGlPluginTopic.REQUIRE_REDRAW) { - return undefined; - } - }; - - return snapshotGetter; - } - - getPublishSubscribeDelegate(): PublishSubscribeDelegate { - return this._publishSubscribeDelegate; - } + getCursor?(pickingInfo: PickingInfo): string | null; + getLayers?(): Layer[]; } export enum DeckGlInstanceManagerTopic { @@ -65,16 +43,27 @@ export type DeckGlInstanceManagerPayloads = { [DeckGlInstanceManagerTopic.REDRAW]: undefined; }; -export class DeckGlInstanceManager implements PublishSubscribe { - private _publishSubscribeDelegate = new PublishSubscribeDelegate(); +type HoverPoint = { + worldCoordinates: number[]; + screenCoordinates: [number, number]; +}; - private _ref: DeckGLRef; - private _hoverPoint: [number, number, number] | null = null; +export class DeckGlInstanceManager + implements PublishSubscribe +{ + private _publishSubscribeDelegate = new PublishSubscribeDelegate(); + + private _ref: DeckGLRef | null; + private _hoverPoint: HoverPoint | null = null; private _plugins: DeckGlPlugin[] = []; - private _layers: Layer[] = []; private _layersIdPluginMap = new Map(); + private _cursor: string = "auto"; - constructor(ref: DeckGLRef) { + constructor(ref: DeckGLRef | null) { + this._ref = ref; + } + + setRef(ref: DeckGLRef | null) { this._ref = ref; } @@ -82,19 +71,17 @@ export class DeckGlInstanceManager implements PublishSubscribe) { - this._layers.push(layers); + redraw() { + this._publishSubscribeDelegate.notifySubscribers(DeckGlInstanceManagerTopic.REDRAW); } - redraw() {} - - getPublishSubscribeDelegate(): PublishSubscribeDelegate { + getPublishSubscribeDelegate(): PublishSubscribeDelegate { return this._publishSubscribeDelegate; } - makeSnapshotGetter(topic: T): () => DeckGlPluginPayloads[T] { + makeSnapshotGetter(topic: T): () => DeckGlInstanceManagerPayloads[T] { const snapshotGetter = (): any => { - if (topic === DeckGlPluginTopic.REQUIRE_REDRAW) { + if (topic === DeckGlInstanceManagerTopic.REDRAW) { return undefined; } }; @@ -106,6 +93,11 @@ export class DeckGlInstanceManager implements PublishSubscribe { + this._hoverPoint = { + worldCoordinates: firstLayerInfo.coordinate, + screenCoordinates: [firstLayerInfo.x, firstLayerInfo.y], + }; + + const layerId = this.getLayerIdFromPickingInfo(firstLayerInfo); + if (!layerId) { + return; + } + + const plugin = this._layersIdPluginMap.get(layerId); + if (!plugin) { + return; + } + + if (event.type === "hover") { + plugin.handleMouseHover?.(firstLayerInfo); + this._cursor = plugin.getCursor?.(firstLayerInfo) ?? "auto"; + } + } + + private getFirstLayerUnderCursorInfo(event: MapMouseEvent): PickingInfo | undefined { + for (const info of event.infos) { + if (info.coordinate && info.x) { + return info; + } + } + + return undefined; + } + + getCursor(cursorState: Parameters>[0]): string { + if (cursorState.isDragging) { + return "grabbing"; + } + + return this._cursor; + } + + makeDeckGlComponentProps(withLayers: Layer[]): Partial { + const layers = [...withLayers]; + for (const plugin of this._plugins) { + const pluginLayers = plugin.getLayers?.() ?? []; + layers.push(...pluginLayers); + for (const layer of pluginLayers) { + this._layersIdPluginMap.set(layer.id, plugin); + } + } return { onDrag: this.handleDrag.bind(this), onMouseEvent: this.handleMouseEvent.bind(this), + getCursor: this.getCursor.bind(this), + layers, }; } } diff --git a/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts index b452ec416..451a49a2b 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts @@ -1,7 +1,13 @@ -import { MapMouseEvent } from "@webviz/subsurface-viewer"; +import addPathIcon from "@assets/add_path.svg"; +import continuePathIcon from "@assets/continue_path.svg"; +import removePathIcon from "@assets/remove_path.svg"; +import { Layer, PickingInfo } from "@deck.gl/core"; +import { PublishSubscribeDelegate } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { DeckGlPlugin } from "./DeckGlInstanceManager"; +import { isEditablePolylineLayerPickingInfo } from "../hooks/editablePolylines/deckGlLayers/EditablePolylineLayer"; + export type Polyline = { id: string; name: string; @@ -36,7 +42,7 @@ enum AppendToPathLocation { END = "end", } -export class PolylinesPlugin extends DeckGlPlugin { +export class PolylinesPlugin extends DeckGlPlugin { private _currentEditingPolylineId: string | null = null; private _currentEditingPolylinePathReferencePointIndex: number | null = null; private _polylines: Polyline[] = []; @@ -44,9 +50,24 @@ export class PolylinesPlugin extends DeckGlPlugin(); + private setCurrentEditingPolylineId(id: string | null): void { this._currentEditingPolylineId = id; - super.getPublishSubscribeDelegate().notifySubscribers(PolylinesPluginTopic.EDITING_POLYLINE_CHANGE); + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.EDITING_POLYLINE_CHANGE); + } + + private getActivePolyline(): Polyline | undefined { + return this._polylines.find((polyline) => polyline.id === this._currentEditingPolylineId); + } + + setEditingMode(mode: PolylineEditingMode): void { + this._editingMode = mode; + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.EDITING_MODE_CHANGE); + } + + getCurrentEditingPolylineId(): string | null { + return this._currentEditingPolylineId; } handleKeyUpEvent(key: string): void { @@ -63,17 +84,53 @@ export class PolylinesPlugin extends DeckGlPlugin[] { + return []; } protected makeSnapshot(topic: T): PolylinesPluginTopicPayloads[T] { From 1716de4ebfee3ab5042dcecfedea4189c3e9d20a Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 29 Jan 2025 15:00:55 +0100 Subject: [PATCH 26/97] wip --- .../view/components/ContextMenu.tsx | 35 ++ .../view/components/ReadoutWrapper.tsx | 12 +- .../deckGlLayers/EditablePolylineLayer.ts | 13 +- .../view/utils/DeckGlInstanceManager.ts | 186 +++++++++-- .../3DViewerNew/view/utils/PolylinesPlugin.ts | 316 +++++++++++++++++- .../LayerFramework/delegates/GroupDelegate.ts | 6 +- .../LayerFramework/delegates/ItemDelegate.ts | 6 +- .../LayerFramework/delegates/LayerDelegate.ts | 6 +- .../delegates/SettingDelegate.ts | 6 +- .../delegates/SettingsContextDelegate.ts | 6 +- .../framework/LayerManager/LayerManager.ts | 6 +- .../_shared/utils/PublishSubscribeDelegate.ts | 24 +- 12 files changed, 541 insertions(+), 81 deletions(-) create mode 100644 frontend/src/modules/3DViewerNew/view/components/ContextMenu.tsx diff --git a/frontend/src/modules/3DViewerNew/view/components/ContextMenu.tsx b/frontend/src/modules/3DViewerNew/view/components/ContextMenu.tsx new file mode 100644 index 000000000..eefea0339 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/components/ContextMenu.tsx @@ -0,0 +1,35 @@ +import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; + +import { DeckGlInstanceManager, DeckGlInstanceManagerTopic } from "../utils/DeckGlInstanceManager"; + +export type ContextMenuProps = { + deckGlManager: DeckGlInstanceManager; +}; + +export function ContextMenu(props: ContextMenuProps): React.ReactNode { + const contextMenu = usePublishSubscribeTopicValue(props.deckGlManager, DeckGlInstanceManagerTopic.CONTEXT_MENU); + + if (!contextMenu) { + return null; + } + + return ( +
+ {contextMenu.items.map((item, index) => ( +
{ + item.onClick(); + }} + > + {item.icon} + {item.label} +
+ ))} +
+ ); +} diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index cbc5b89b0..d08065031 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -3,14 +3,16 @@ import React from "react"; import { Layer as DeckGlLayer, PickingInfo } from "@deck.gl/core"; import { DeckGLRef } from "@deck.gl/react"; import { SubsurfaceViewerWithCameraState } from "@modules/_shared/components/SubsurfaceViewerWithCameraState"; +import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { BoundingBox3D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; +import { ContextMenu } from "./ContextMenu"; import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; import { Toolbar } from "./Toolbar"; import { Polyline, PolylineEditingMode } from "../hooks/editablePolylines/types"; -import { DeckGlInstanceManager } from "../utils/DeckGlInstanceManager"; +import { DeckGlInstanceManager, DeckGlInstanceManagerTopic } from "../utils/DeckGlInstanceManager"; import { PolylinesPlugin } from "../utils/PolylinesPlugin"; export type ReadooutWrapperProps = { @@ -28,12 +30,19 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { ); const [polylinesPlugin, setPolylinesPlugin] = React.useState(new PolylinesPlugin(deckGlManager)); + usePublishSubscribeTopicValue(deckGlManager, DeckGlInstanceManagerTopic.REDRAW); + React.useEffect(function setupDeckGlManager() { const manager = new DeckGlInstanceManager(deckGlRef.current); setDeckGlManager(manager); const polylinesPlugin = new PolylinesPlugin(manager); manager.addPlugin(polylinesPlugin); + setPolylinesPlugin(polylinesPlugin); + + return function cleanupDeckGlManager() { + manager.beforeDestroy(); + }; }, []); const [cameraPositionSetByAction, setCameraPositionSetByAction] = React.useState(null); @@ -152,6 +161,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { ))} )*/} + d.from, getTargetPosition: (d) => d.to, - getColor: [230, 136, 21, 100], + getColor: [polyline.color[0], polyline.color[1], polyline.color[2], 100], getWidth: 10, widthUnits: "meters", widthMinPixels: 3, @@ -127,14 +127,11 @@ export class EditablePolylineLayer extends CompositeLayer d, - getFillColor: [230, 136, 21, 255], - getLineColor: [230, 136, 21, 255], - getLineWidth: 10, + getFillColor: [polyline.color[0], polyline.color[1], polyline.color[2], 100], getRadius: 10, - lineWidthMinPixels: 3, - lineWidthMaxPixels: 5, radiusUnits: "pixels", - lineWidthUnits: "pixels", + radiusMinPixels: 5, + radiusMaxPixels: 10, pickable: false, parameters: { depthTest: false, @@ -244,7 +241,7 @@ export class EditablePolylineLayer extends CompositeLayer { if ( diff --git a/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts b/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts index f4d713259..900807505 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts @@ -1,19 +1,23 @@ /* This manager is responsible for managing plugins for DeckGL, forwarding events to them, and adding/adjusting layers based on the plugins' responses. */ +import React from "react"; + import { Layer, PickingInfo } from "@deck.gl/core"; import { DeckGLProps, DeckGLRef } from "@deck.gl/react"; import { PublishSubscribe, PublishSubscribeDelegate } from "@modules_shared/utils/PublishSubscribeDelegate"; import { MapMouseEvent, SubsurfaceViewerProps } from "@webviz/subsurface-viewer"; -export enum DeckGlPluginTopic { - REQUIRE_REDRAW = "REQUIRE_REDRAW", -} - -export type DeckGlPluginPayloads = { - [DeckGlPluginTopic.REQUIRE_REDRAW]: undefined; +export type ContextMenuItem = { + icon?: React.ReactNode; + label: string; + onClick: () => void; }; +export type ContextMenu = { + position: { x: number; y: number }; + items: ContextMenuItem[]; +}; export class DeckGlPlugin { private _manager: DeckGlInstanceManager; @@ -21,26 +25,43 @@ export class DeckGlPlugin { this._manager = manager; } - private requireRedraw() { + protected requireRedraw() { this._manager.redraw(); } - handleDragStart?(pickingInfo: PickingInfo): void; + protected requestDisablePanning() { + this._manager.disablePanning(); + } + + protected requestEnablePanning() { + this._manager.enablePanning(); + } + + protected getFirstLayerUnderCursorInfo(x: number, y: number): PickingInfo | undefined { + return this._manager.pickFirstLayerUnderCursorInfo(x, y); + } + handleDrag?(pickingInfo: PickingInfo): void; - handleMouseHover?(pickingInfo: PickingInfo): void; - handleMouseClick?(pickingInfo: PickingInfo): void; + handleLayerHover?(pickingInfo: PickingInfo): void; + handleLayerClick?(pickingInfo: PickingInfo): void; + handleClickAway?(): void; + handleGlobalMouseHover?(pickingInfo: PickingInfo): void; + handleGlobalMouseClick?(pickingInfo: PickingInfo): boolean; handleKeyUpEvent?(key: string): void; handleKeyDownEvent?(key: string): void; getCursor?(pickingInfo: PickingInfo): string | null; getLayers?(): Layer[]; + getContextMenuItems?(pickingInfo: PickingInfo): ContextMenuItem[]; } export enum DeckGlInstanceManagerTopic { REDRAW = "REDRAW", + CONTEXT_MENU = "CONTEXT_MENU", } export type DeckGlInstanceManagerPayloads = { - [DeckGlInstanceManagerTopic.REDRAW]: undefined; + [DeckGlInstanceManagerTopic.REDRAW]: number; + [DeckGlInstanceManagerTopic.CONTEXT_MENU]: ContextMenu | null; }; type HoverPoint = { @@ -48,42 +69,106 @@ type HoverPoint = { screenCoordinates: [number, number]; }; -export class DeckGlInstanceManager - implements PublishSubscribe -{ - private _publishSubscribeDelegate = new PublishSubscribeDelegate(); +type KeyboardEventListener = (event: KeyboardEvent) => void; + +export class DeckGlInstanceManager implements PublishSubscribe { + private _publishSubscribeDelegate = new PublishSubscribeDelegate(); private _ref: DeckGLRef | null; private _hoverPoint: HoverPoint | null = null; private _plugins: DeckGlPlugin[] = []; private _layersIdPluginMap = new Map(); private _cursor: string = "auto"; + private _redrawCycle: number = 0; + private _eventListeners: KeyboardEventListener[] = []; + private _contextMenu: ContextMenu | null = null; constructor(ref: DeckGLRef | null) { this._ref = ref; + this.addKeyboardEventListeners(); } setRef(ref: DeckGLRef | null) { this._ref = ref; } + private addKeyboardEventListeners() { + const handleKeyDown = this.handleKeyDown.bind(this); + const handleKeyUp = this.handleKeyUp.bind(this); + + this._eventListeners = [handleKeyDown, handleKeyUp]; + + document.addEventListener("keyup", handleKeyUp); + document.addEventListener("keydown", handleKeyDown); + } + + private maybeRemoveKeyboardEventListeners() { + for (const listener of this._eventListeners) { + document.removeEventListener("keydown", listener); + } + } + + private handleKeyDown(event: KeyboardEvent) { + for (const plugin of this._plugins) { + plugin.handleKeyDownEvent?.(event.key); + } + } + + private handleKeyUp(event: KeyboardEvent) { + for (const plugin of this._plugins) { + plugin.handleKeyUpEvent?.(event.key); + } + } + addPlugin(plugin: DeckGlPlugin) { this._plugins.push(plugin); } redraw() { + this._redrawCycle++; this._publishSubscribeDelegate.notifySubscribers(DeckGlInstanceManagerTopic.REDRAW); } - getPublishSubscribeDelegate(): PublishSubscribeDelegate { + disablePanning() { + if (!this._ref) { + return; + } + + this._ref.deck?.setProps({ + controller: { + dragPan: false, + dragRotate: false, + }, + }); + } + + enablePanning() { + if (!this._ref) { + return; + } + + this._ref.deck?.setProps({ + controller: { + dragRotate: true, + dragPan: true, + }, + }); + } + + getPublishSubscribeDelegate(): PublishSubscribeDelegate { return this._publishSubscribeDelegate; } makeSnapshotGetter(topic: T): () => DeckGlInstanceManagerPayloads[T] { const snapshotGetter = (): any => { if (topic === DeckGlInstanceManagerTopic.REDRAW) { - return undefined; + return this._redrawCycle; + } + if (topic === DeckGlInstanceManagerTopic.CONTEXT_MENU) { + return this._contextMenu; } + + throw new Error(`Unknown topic ${topic}`); }; return snapshotGetter; @@ -93,11 +178,6 @@ export class DeckGlInstanceManager return pickingInfo.layer?.id; } - private setCursor(cursor: string) { - this._cursor = cursor; - this._publishSubscribeDelegate.notifySubscribers(DeckGlInstanceManagerTopic.REDRAW); - } - handleDrag(pickingInfo: PickingInfo): void { const layerId = this.getLayerIdFromPickingInfo(pickingInfo); if (!layerId) { @@ -122,11 +202,12 @@ export class DeckGlInstanceManager if (!plugin) { return; } - - plugin.handleDragStart?.(pickingInfo); } handleMouseEvent(event: MapMouseEvent) { + this._contextMenu = null; + this._publishSubscribeDelegate.notifySubscribers(DeckGlInstanceManagerTopic.CONTEXT_MENU); + const firstLayerInfo = this.getFirstLayerUnderCursorInfo(event); if (!firstLayerInfo || !firstLayerInfo.coordinate) { this._hoverPoint = null; @@ -139,21 +220,58 @@ export class DeckGlInstanceManager }; const layerId = this.getLayerIdFromPickingInfo(firstLayerInfo); - if (!layerId) { + const plugin = this._layersIdPluginMap.get(layerId ?? ""); + if (layerId && plugin) { + if (event.type === "hover") { + plugin.handleLayerHover?.(firstLayerInfo); + this._cursor = plugin.getCursor?.(firstLayerInfo) ?? "auto"; + } + + if (event.type === "click") { + plugin.handleLayerClick?.(firstLayerInfo); + } + + if (event.type === "contextmenu") { + const contextMenuItems = plugin.getContextMenuItems?.(firstLayerInfo) ?? []; + this._contextMenu = { + position: { x: event.x ?? 0, y: event.y ?? 0 }, + items: contextMenuItems, + }; + this._publishSubscribeDelegate.notifySubscribers(DeckGlInstanceManagerTopic.CONTEXT_MENU); + } + return; } - const plugin = this._layersIdPluginMap.get(layerId); - if (!plugin) { - return; + const pluginsThatDidNotAcceptEvent: DeckGlPlugin[] = []; + for (const plugin of this._plugins) { + if (event.type === "hover") { + plugin.handleGlobalMouseHover?.(firstLayerInfo); + this._cursor = "auto"; + } else if (event.type === "click") { + const accepted = plugin.handleGlobalMouseClick?.(firstLayerInfo); + if (!accepted) { + pluginsThatDidNotAcceptEvent.push(plugin); + } + } } - if (event.type === "hover") { - plugin.handleMouseHover?.(firstLayerInfo); - this._cursor = plugin.getCursor?.(firstLayerInfo) ?? "auto"; + if (event.type === "click") { + for (const plugin of pluginsThatDidNotAcceptEvent) { + plugin.handleClickAway?.(); + } } } + pickFirstLayerUnderCursorInfo(x: number, y: number): PickingInfo | undefined { + if (!this._ref?.deck) { + return undefined; + } + + const layer = this._ref.deck.pickObject({ x, y, radius: 10, unproject3D: true }) ?? undefined; + return layer; + } + private getFirstLayerUnderCursorInfo(event: MapMouseEvent): PickingInfo | undefined { for (const info of event.infos) { if (info.coordinate && info.x) { @@ -184,8 +302,12 @@ export class DeckGlInstanceManager return { onDrag: this.handleDrag.bind(this), onMouseEvent: this.handleMouseEvent.bind(this), - getCursor: this.getCursor.bind(this), + getCursor: (state) => this.getCursor(state), layers, }; } + + beforeDestroy() { + this.maybeRemoveKeyboardEventListeners(); + } } diff --git a/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts index 451a49a2b..0e249ac18 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts @@ -4,15 +4,22 @@ import removePathIcon from "@assets/remove_path.svg"; import { Layer, PickingInfo } from "@deck.gl/core"; import { PublishSubscribeDelegate } from "@modules/_shared/utils/PublishSubscribeDelegate"; -import { DeckGlPlugin } from "./DeckGlInstanceManager"; +import { v4 } from "uuid"; -import { isEditablePolylineLayerPickingInfo } from "../hooks/editablePolylines/deckGlLayers/EditablePolylineLayer"; +import { ContextMenuItem, DeckGlPlugin } from "./DeckGlInstanceManager"; + +import { + AllowHoveringOf, + EditablePolylineLayer, + isEditablePolylineLayerPickingInfo, +} from "../hooks/editablePolylines/deckGlLayers/EditablePolylineLayer"; +import { PolylinesLayer, isPolylinesLayerPickingInfo } from "../hooks/editablePolylines/deckGlLayers/PolylinesLayer"; export type Polyline = { id: string; name: string; color: [number, number, number, number]; - path: [number, number, number][]; + path: number[][]; }; export enum PolylineEditingMode { @@ -47,10 +54,12 @@ export class PolylinesPlugin extends DeckGlPlugin { private _currentEditingPolylinePathReferencePointIndex: number | null = null; private _polylines: Polyline[] = []; private _editingMode: PolylineEditingMode = PolylineEditingMode.NONE; - private _draggedPointIndex: number | null = null; + private _draggedPathPointIndex: number | null = null; private _appendToPathLocation: AppendToPathLocation = AppendToPathLocation.END; + private _selectedPolylineId: string | null = null; + private _hoverPoint: number[] | null = null; - private _publishSubscribeDelegate = new PublishSubscribeDelegate(); + private _publishSubscribeDelegate = new PublishSubscribeDelegate(); private setCurrentEditingPolylineId(id: string | null): void { this._currentEditingPolylineId = id; @@ -63,7 +72,12 @@ export class PolylinesPlugin extends DeckGlPlugin { setEditingMode(mode: PolylineEditingMode): void { this._editingMode = mode; + if ([PolylineEditingMode.NONE, PolylineEditingMode.IDLE].includes(mode)) { + this._hoverPoint = null; + this._currentEditingPolylinePathReferencePointIndex = null; + } this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.EDITING_MODE_CHANGE); + this.requireRedraw(); } getCurrentEditingPolylineId(): string | null { @@ -73,21 +87,235 @@ export class PolylinesPlugin extends DeckGlPlugin { handleKeyUpEvent(key: string): void { if (key === "Escape") { if (this._editingMode === PolylineEditingMode.NONE) { - this.setCurrentEditingPolylineId(null); this._currentEditingPolylinePathReferencePointIndex = null; + this.requireRedraw(); + return; } - if (this._editingMode == PolylineEditingMode.IDLE) { + if (this._editingMode === PolylineEditingMode.IDLE) { this._currentEditingPolylinePathReferencePointIndex = null; + this._hoverPoint = null; + this.requireRedraw(); + return; } - this._editingMode = PolylineEditingMode.IDLE; + this._hoverPoint = null; + this.setEditingMode(PolylineEditingMode.IDLE); + this.requireRedraw(); } } - handleMouseClick(pickingInfo: PickingInfo): void { + handleLayerClick(pickingInfo: PickingInfo): void { + if (this._editingMode === PolylineEditingMode.NONE || this._editingMode === PolylineEditingMode.IDLE) { + if (isPolylinesLayerPickingInfo(pickingInfo)) { + this._selectedPolylineId = pickingInfo.polylineId ?? null; + this.requireRedraw(); + } + return; + } + + if (!isEditablePolylineLayerPickingInfo(pickingInfo)) { + return; + } + + const activePolyline = this.getActivePolyline(); + if (!activePolyline) { + return; + } + + if (pickingInfo.editableEntity?.type === "point") { + if (![PolylineEditingMode.DRAW, PolylineEditingMode.REMOVE_POINT].includes(this._editingMode)) { + return; + } + + const index = pickingInfo.editableEntity.index; + if (this._editingMode === PolylineEditingMode.DRAW) { + if ( + (index === 0 || index === activePolyline.path.length - 1) && + this._currentEditingPolylinePathReferencePointIndex !== index + ) { + this._appendToPathLocation = index === 0 ? AppendToPathLocation.START : AppendToPathLocation.END; + this._currentEditingPolylinePathReferencePointIndex = index; + this.requireRedraw(); + return; + } + } + + const newPath = activePolyline.path.filter((_, i) => i !== index); + let newReferencePathPointIndex: number | null = null; + if (this._currentEditingPolylinePathReferencePointIndex !== null) { + newReferencePathPointIndex = Math.max(0, this._currentEditingPolylinePathReferencePointIndex - 1); + if (index > this._currentEditingPolylinePathReferencePointIndex) { + newReferencePathPointIndex = this._currentEditingPolylinePathReferencePointIndex; + } + if (activePolyline.path.length - 1 < 1) { + newReferencePathPointIndex = null; + } + } + + this.updateActivePolylinePath(newPath); + this._currentEditingPolylinePathReferencePointIndex = newReferencePathPointIndex; + this.requireRedraw(); + return; + } + + if (pickingInfo.editableEntity?.type === "line") { + if (![PolylineEditingMode.DRAW, PolylineEditingMode.ADD_POINT].includes(this._editingMode)) { + return; + } + + if (!pickingInfo.coordinate) { + return; + } + + const index = pickingInfo.editableEntity.index; + const newPath = [...activePolyline.path]; + newPath.splice(index + 1, 0, [...pickingInfo.coordinate]); + this.updateActivePolylinePath(newPath); + + let newReferencePathPointIndex: number | null = null; + if (this._currentEditingPolylinePathReferencePointIndex !== null) { + newReferencePathPointIndex = this._currentEditingPolylinePathReferencePointIndex + 1; + if (index > this._currentEditingPolylinePathReferencePointIndex) { + newReferencePathPointIndex = this._currentEditingPolylinePathReferencePointIndex; + } + } + + this._currentEditingPolylinePathReferencePointIndex = newReferencePathPointIndex; + this.requireRedraw(); + } + } + + private updateActivePolylinePath(newPath: number[][]): void { + const activePolyline = this.getActivePolyline(); + if (!activePolyline) { + return; + } + + this._polylines = this._polylines.map((polyline) => { + if (polyline.id === activePolyline.id) { + return { + ...polyline, + path: newPath, + }; + } + return polyline; + }); + } + + handleClickAway(): void { + this._selectedPolylineId = null; + this.requireRedraw(); + } + + handleGlobalMouseHover(pickingInfo: PickingInfo): void { + if (this._editingMode !== PolylineEditingMode.DRAW) { + return; + } + + if (!pickingInfo.coordinate) { + return; + } + + this._hoverPoint = pickingInfo.coordinate; + this.requireRedraw(); + } + + handleGlobalMouseClick(pickingInfo: PickingInfo): boolean { if (this._editingMode === PolylineEditingMode.NONE) { + return false; + } + + if (!pickingInfo.coordinate) { + return false; + } + + const activePolyline = this.getActivePolyline(); + if (!activePolyline && this._editingMode === PolylineEditingMode.DRAW) { + const id = v4(); + this._polylines.push({ + id, + name: "New polyline", + color: [255, 0, 0, 255], + path: [[...pickingInfo.coordinate]], + }); + this._polylines = [...this._polylines]; + this._currentEditingPolylinePathReferencePointIndex = 0; + this.setCurrentEditingPolylineId(id); + this.requireRedraw(); + } else if (activePolyline) { + if (this._currentEditingPolylinePathReferencePointIndex === null) { + this.setCurrentEditingPolylineId(null); + this.setEditingMode(PolylineEditingMode.IDLE); + this.requireRedraw(); + return true; + } + + if (this._editingMode === PolylineEditingMode.DRAW) { + this.appendToActivePolylinePath(pickingInfo.coordinate); + this.requireRedraw(); + return true; + } + } + + return false; + } + + private appendToActivePolylinePath(point: number[]): void { + const activePolyline = this.getActivePolyline(); + if (!activePolyline) { + return; + } + + const newPath = [...activePolyline.path]; + if (this._appendToPathLocation === AppendToPathLocation.START) { + newPath.unshift(point); + this._currentEditingPolylinePathReferencePointIndex = 0; + } else { + newPath.push(point); + this._currentEditingPolylinePathReferencePointIndex = newPath.length - 1; + } + + this.updateActivePolylinePath(newPath); + } + + handleDragStart(pickingInfo: PickingInfo): void { + if (!isEditablePolylineLayerPickingInfo(pickingInfo)) { + return; + } + + if (pickingInfo.editableEntity?.type === "point") { + this._draggedPathPointIndex = pickingInfo.index; + this.requestDisablePanning(); + } + } + + handleDrag(pickingInfo: PickingInfo): void { + if (this._draggedPathPointIndex === null || !pickingInfo.coordinate) { + return; + } + + const activePolyline = this.getActivePolyline(); + if (!activePolyline) { return; } + + // Take first layer under cursor to get coordinates for the polyline point + // An alternative would be to store a reference to the layer the polyline was first created upon + // and always try to use that layer to get the coordinates + const layerUnderCursor = this.getFirstLayerUnderCursorInfo(pickingInfo.x, pickingInfo.y); + if (!layerUnderCursor) { + return; + } + + const newPath = [...activePolyline.path]; + newPath[this._draggedPathPointIndex] = [...pickingInfo.coordinate]; + this.updateActivePolylinePath(newPath); + this.requireRedraw(); + } + + handleDragEnd(): void { + this._draggedPathPointIndex = null; + this.requestEnablePanning(); } getCursor(pickingInfo: PickingInfo): string | null { @@ -113,6 +341,7 @@ export class PolylinesPlugin extends DeckGlPlugin { const index = pickingInfo.index; if ( (index === 0 || index === activePolyline.path.length - 1) && + index !== this._currentEditingPolylinePathReferencePointIndex && this._editingMode === PolylineEditingMode.DRAW ) { return `url(${continuePathIcon}) 4 2, crosshair`; @@ -129,8 +358,75 @@ export class PolylinesPlugin extends DeckGlPlugin { return "auto"; } + getContextMenuItems(pickingInfo: PickingInfo): ContextMenuItem[] { + if (this._editingMode !== PolylineEditingMode.IDLE) { + return []; + } + + if (!isPolylinesLayerPickingInfo(pickingInfo) || !pickingInfo.polylineId) { + return []; + } + + return [ + { + label: "Edit", + onClick: () => { + this.setCurrentEditingPolylineId(pickingInfo.polylineId ?? null); + this.requireRedraw(); + }, + }, + { + label: "Delete", + onClick: () => { + this._polylines = this._polylines.filter((polyline) => polyline.id !== pickingInfo.polylineId); + this.setCurrentEditingPolylineId(null); + this.requireRedraw(); + }, + }, + ]; + } + getLayers(): Layer[] { - return []; + const layers: Layer[] = [ + new PolylinesLayer({ + id: "polylines-layer", + polylines: this._polylines.filter((polyline) => polyline.id !== this._currentEditingPolylineId), + selectedPolylineId: + this._editingMode === PolylineEditingMode.NONE ? undefined : this._selectedPolylineId ?? undefined, + hoverable: this._editingMode === PolylineEditingMode.IDLE, + }), + ]; + + let allowHoveringOf = AllowHoveringOf.NONE; + if (this._editingMode === PolylineEditingMode.DRAW) { + allowHoveringOf = AllowHoveringOf.LINES_AND_POINTS; + } + if (this._editingMode === PolylineEditingMode.ADD_POINT) { + allowHoveringOf = AllowHoveringOf.LINES; + } + if (this._editingMode === PolylineEditingMode.REMOVE_POINT) { + allowHoveringOf = AllowHoveringOf.POINTS; + } + + const activePolyline = this.getActivePolyline(); + if (activePolyline) { + layers.push( + new EditablePolylineLayer({ + id: "editable-polylines-layer", + polyline: activePolyline, + mouseHoverPoint: this._hoverPoint ?? undefined, + referencePathPointIndex: + this._editingMode === PolylineEditingMode.DRAW + ? this._currentEditingPolylinePathReferencePointIndex ?? undefined + : undefined, + onDragStart: this.handleDragStart.bind(this), + onDragEnd: this.handleDragEnd.bind(this), + allowHoveringOf, + }) + ); + } + + return layers; } protected makeSnapshot(topic: T): PolylinesPluginTopicPayloads[T] { diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/GroupDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/GroupDelegate.ts index 8580f6750..ed4c37f79 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/GroupDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/GroupDelegate.ts @@ -24,11 +24,11 @@ export type GroupDelegateTopicPayloads = { * It provides methods for adding, removing, and moving children, as well as for serializing and deserializing children. * The class also provides methods for finding children and descendants based on a predicate. */ -export class GroupDelegate implements PublishSubscribe { +export class GroupDelegate implements PublishSubscribe { private _owner: Item | null; private _color: string | null = null; private _children: Item[] = []; - private _publishSubscribeDelegate = new PublishSubscribeDelegate(); + private _publishSubscribeDelegate = new PublishSubscribeDelegate(); private _unsubscribeHandlerDelegate = new UnsubscribeHandlerDelegate(); private _treeRevisionNumber: number = 0; private _deserializing = false; @@ -165,7 +165,7 @@ export class GroupDelegate implements PublishSubscribe { + getPublishSubscribeDelegate(): PublishSubscribeDelegate { return this._publishSubscribeDelegate; } diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/ItemDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/ItemDelegate.ts index 95ea39f3d..ac3866521 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/ItemDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/ItemDelegate.ts @@ -23,14 +23,14 @@ export type ItemDelegatePayloads = { * The ItemDelegate class is responsible for managing the basic properties of an item. * It provides methods for setting and getting the id, parent group, name, visibility, and expansion state of the item. */ -export class ItemDelegate implements PublishSubscribe { +export class ItemDelegate implements PublishSubscribe { private _id: string; private _name: string; private _visible: boolean = true; private _expanded: boolean = true; private _parentGroup: GroupDelegate | null = null; private _layerManager: LayerManager; - private _publishSubscribeDelegate = new PublishSubscribeDelegate(); + private _publishSubscribeDelegate = new PublishSubscribeDelegate(); constructor(name: string, layerManager: LayerManager) { this._id = v4(); @@ -118,7 +118,7 @@ export class ItemDelegate implements PublishSubscribe { + getPublishSubscribeDelegate(): PublishSubscribeDelegate { return this._publishSubscribeDelegate; } diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts index a36fcce71..f7adfdd34 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts @@ -42,14 +42,14 @@ export type LayerDelegatePayloads = { * It also manages the status of the layer (loading, success, error). */ export class LayerDelegate - implements PublishSubscribe> + implements PublishSubscribe> { private _owner: Layer; private _settingsContext: SettingsContext; private _layerManager: LayerManager; private _unsubscribeHandler: UnsubscribeHandlerDelegate = new UnsubscribeHandlerDelegate(); private _cancellationPending: boolean = false; - private _publishSubscribeDelegate = new PublishSubscribeDelegate(); + private _publishSubscribeDelegate = new PublishSubscribeDelegate>(); private _queryKeys: unknown[][] = []; private _status: LayerStatus = LayerStatus.IDLE; private _data: TData | null = null; @@ -191,7 +191,7 @@ export class LayerDelegate return snapshotGetter; } - getPublishSubscribeDelegate(): PublishSubscribeDelegate { + getPublishSubscribeDelegate(): PublishSubscribeDelegate> { return this._publishSubscribeDelegate; } diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts index 93127f5da..838f6a34b 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts @@ -33,12 +33,12 @@ export type SettingTopicPayloads = { * It provides a method for setting available values, which are used to validate the setting value or applying a fixup if the value is invalid. * It provides methods for setting and getting the value and its states, checking if the value is valid, and setting the value as overridden or persisted. */ -export class SettingDelegate implements PublishSubscribe> { +export class SettingDelegate implements PublishSubscribe> { private _id: string; private _owner: Setting; private _value: TValue; private _isValueValid: boolean = false; - private _publishSubscribeDelegate = new PublishSubscribeDelegate(); + private _publishSubscribeDelegate = new PublishSubscribeDelegate>(); private _availableValues: AvailableValuesType = [] as unknown as AvailableValuesType; private _overriddenValue: TValue | undefined = undefined; private _loading: boolean = false; @@ -230,7 +230,7 @@ export class SettingDelegate implements PublishSubscribe { + getPublishSubscribeDelegate(): PublishSubscribeDelegate> { return this._publishSubscribeDelegate; } diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts index 64210ea68..1865aded7 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts @@ -50,13 +50,13 @@ export type SettingsContextDelegateState - implements PublishSubscribe + implements PublishSubscribe { private _parentContext: SettingsContext; private _layerManager: LayerManager; private _settings: { [K in TKey]: Setting } = {} as { [K in TKey]: Setting }; private _overriddenSettings: { [K in TKey]: TSettings[K] } = {} as { [K in TKey]: TSettings[K] }; - private _publishSubscribeDelegate = new PublishSubscribeDelegate(); + private _publishSubscribeDelegate = new PublishSubscribeDelegate(); private _unsubscribeHandler: UnsubscribeHandlerDelegate = new UnsubscribeHandlerDelegate(); private _loadingState: SettingsContextLoadingState = SettingsContextLoadingState.LOADING; @@ -217,7 +217,7 @@ export class SettingsContextDelegate { + getPublishSubscribeDelegate(): PublishSubscribeDelegate { return this._publishSubscribeDelegate; } diff --git a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts index 5935bb46f..b654821a9 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts +++ b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts @@ -50,12 +50,12 @@ export type GlobalSettings = { * It does also serve as a provider of the QueryClient and WorkbenchSession. */ -export class LayerManager implements Group, PublishSubscribe { +export class LayerManager implements Group, PublishSubscribe { private _workbenchSession: WorkbenchSession; private _workbenchSettings: WorkbenchSettings; private _groupDelegate: GroupDelegate; private _queryClient: QueryClient; - private _publishSubscribeDelegate = new PublishSubscribeDelegate(); + private _publishSubscribeDelegate = new PublishSubscribeDelegate(); private _itemDelegate: ItemDelegate; private _layerDataRevision: number = 0; private _globalSettings: GlobalSettings; @@ -166,7 +166,7 @@ export class LayerManager implements Group, PublishSubscribe { + getPublishSubscribeDelegate(): PublishSubscribeDelegate { return this._publishSubscribeDelegate; } diff --git a/frontend/src/modules/_shared/utils/PublishSubscribeDelegate.ts b/frontend/src/modules/_shared/utils/PublishSubscribeDelegate.ts index 567fd3bfc..994e8ce08 100644 --- a/frontend/src/modules/_shared/utils/PublishSubscribeDelegate.ts +++ b/frontend/src/modules/_shared/utils/PublishSubscribeDelegate.ts @@ -1,23 +1,23 @@ import React from "react"; -export type TopicPayloads = Record; +export type TopicPayloads = Record; -export interface PublishSubscribe> { - makeSnapshotGetter(topic: T): () => TTopicPayloads[T]; - getPublishSubscribeDelegate(): PublishSubscribeDelegate; +export interface PublishSubscribe { + makeSnapshotGetter(topic: T): () => TTopicPayloads[T]; + getPublishSubscribeDelegate(): PublishSubscribeDelegate; } -export class PublishSubscribeDelegate { - private _subscribers = new Map void>>(); +export class PublishSubscribeDelegate { + private _subscribers = new Map void>>(); - notifySubscribers(topic: TTopic): void { + notifySubscribers(topic: keyof TTopicPayloads): void { const subscribers = this._subscribers.get(topic); if (subscribers) { subscribers.forEach((subscriber) => subscriber()); } } - makeSubscriberFunction(topic: TTopic): (onStoreChangeCallback: () => void) => () => void { + makeSubscriberFunction(topic: keyof TTopicPayloads): (onStoreChangeCallback: () => void) => () => void { // Using arrow function in order to keep "this" in context const subscriber = (onStoreChangeCallback: () => void): (() => void) => { const subscribers = this._subscribers.get(topic) || new Set(); @@ -33,10 +33,10 @@ export class PublishSubscribeDelegate { } } -export function usePublishSubscribeTopicValue>( - publishSubscribe: PublishSubscribe, - topic: TTopic -): TTopicPayloads[TTopic] { +export function usePublishSubscribeTopicValue< + TTopicPayloads extends TopicPayloads, + TTopic extends keyof TTopicPayloads +>(publishSubscribe: PublishSubscribe, topic: TTopic): TTopicPayloads[TTopic] { const value = React.useSyncExternalStore( publishSubscribe.getPublishSubscribeDelegate().makeSubscriberFunction(topic), publishSubscribe.makeSnapshotGetter(topic) From 1be0610b5030c5b04d06178a6956e7cf4c976bb3 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 29 Jan 2025 15:07:24 +0100 Subject: [PATCH 27/97] wip --- .../modules/_shared/LayerFramework/delegates/SettingDelegate.ts | 2 +- .../LayerFramework/framework/LayerManager/LayerManager.ts | 2 +- .../_shared/LayerFramework/settings/SettingComponent.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts index 838f6a34b..1c04b1128 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/SettingDelegate.ts @@ -20,7 +20,7 @@ export enum SettingTopic { export type SettingTopicPayloads = { [SettingTopic.VALUE_CHANGED]: TValue; [SettingTopic.VALIDITY_CHANGED]: boolean; - [SettingTopic.AVAILABLE_VALUES_CHANGED]: Exclude[]; + [SettingTopic.AVAILABLE_VALUES_CHANGED]: AvailableValuesType; [SettingTopic.OVERRIDDEN_CHANGED]: TValue | undefined; [SettingTopic.LOADING_STATE_CHANGED]: boolean; [SettingTopic.INIT_STATE_CHANGED]: boolean; diff --git a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts index b654821a9..293c0986a 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts +++ b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts @@ -30,7 +30,7 @@ export type LayerManagerTopicPayload = { [LayerManagerTopic.SETTINGS_CHANGED]: void; [LayerManagerTopic.AVAILABLE_SETTINGS_CHANGED]: void; [LayerManagerTopic.LAYER_DATA_REVISION]: number; - [LayerManagerTopic.GLOBAL_SETTINGS_CHANGED]: void; + [LayerManagerTopic.GLOBAL_SETTINGS_CHANGED]: GlobalSettings; [LayerManagerTopic.SHARED_SETTINGS_CHANGED]: void; }; diff --git a/frontend/src/modules/_shared/LayerFramework/settings/SettingComponent.tsx b/frontend/src/modules/_shared/LayerFramework/settings/SettingComponent.tsx index 35def617a..2bfa59ba1 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/SettingComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/SettingComponent.tsx @@ -87,7 +87,7 @@ export function SettingComponent(props: SettingComponentProps): value={value} isValueValid={isValid} isOverridden={overriddenValue !== undefined} - overriddenValue={overriddenValue} + overriddenValue={overriddenValue ?? null} availableValues={availableValues} globalSettings={globalSettings} workbenchSession={props.manager.getWorkbenchSession()} From f787d2dcb0369fe99e977cdc67e8da5134529728 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 29 Jan 2025 17:03:32 +0100 Subject: [PATCH 28/97] wip --- .../view/components/ContextMenu.tsx | 38 +++++- .../view/components/ReadoutWrapper.tsx | 112 ++++++------------ .../3DViewerNew/view/components/Toolbar.tsx | 40 ++++--- .../deckGlLayers/PolylinesLayer.ts | 4 +- .../view/utils/DeckGlInstanceManager.ts | 30 +++-- ...PolylinesPlugin.ts => PolylinesPlugin.tsx} | 90 +++++++++----- 6 files changed, 171 insertions(+), 143 deletions(-) rename frontend/src/modules/3DViewerNew/view/utils/{PolylinesPlugin.ts => PolylinesPlugin.tsx} (85%) diff --git a/frontend/src/modules/3DViewerNew/view/components/ContextMenu.tsx b/frontend/src/modules/3DViewerNew/view/components/ContextMenu.tsx index eefea0339..2eea7315f 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ContextMenu.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ContextMenu.tsx @@ -1,32 +1,60 @@ +import React from "react"; + import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; -import { DeckGlInstanceManager, DeckGlInstanceManagerTopic } from "../utils/DeckGlInstanceManager"; +import { isEqual } from "lodash"; + +import { + ContextMenu as ContextMenuType, + DeckGlInstanceManager, + DeckGlInstanceManagerTopic, +} from "../utils/DeckGlInstanceManager"; export type ContextMenuProps = { deckGlManager: DeckGlInstanceManager; }; export function ContextMenu(props: ContextMenuProps): React.ReactNode { + const [visible, setVisible] = React.useState(false); + const [prevContextMenu, setPrevContextMenu] = React.useState(null); const contextMenu = usePublishSubscribeTopicValue(props.deckGlManager, DeckGlInstanceManagerTopic.CONTEXT_MENU); - if (!contextMenu) { + React.useEffect(function handleMount() { + function hideContextMenu() { + setVisible(false); + } + + window.addEventListener("blur", hideContextMenu); + + return function handleUnmount() { + window.removeEventListener("blur", hideContextMenu); + }; + }, []); + + if (!isEqual(prevContextMenu, contextMenu)) { + setPrevContextMenu(contextMenu); + setVisible(true); + } + + if (!contextMenu || !visible || !contextMenu.items.length) { return null; } return (
{contextMenu.items.map((item, index) => (
{ item.onClick(); + setVisible(false); }} > - {item.icon} + {item.icon ? React.cloneElement(item.icon, { fontSize: "small" }) : null} {item.label}
))} diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index d08065031..3a1bcdce9 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { Layer as DeckGlLayer, PickingInfo } from "@deck.gl/core"; +import { Layer as DeckGlLayer } from "@deck.gl/core"; import { DeckGLRef } from "@deck.gl/react"; import { SubsurfaceViewerWithCameraState } from "@modules/_shared/components/SubsurfaceViewerWithCameraState"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; @@ -11,7 +11,7 @@ import { ContextMenu } from "./ContextMenu"; import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; import { Toolbar } from "./Toolbar"; -import { Polyline, PolylineEditingMode } from "../hooks/editablePolylines/types"; +import { Polyline } from "../hooks/editablePolylines/types"; import { DeckGlInstanceManager, DeckGlInstanceManagerTopic } from "../utils/DeckGlInstanceManager"; import { PolylinesPlugin } from "../utils/PolylinesPlugin"; @@ -50,31 +50,8 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const [layerPickingInfo, setLayerPickingInfo] = React.useState([]); const [gridVisible, setGridVisible] = React.useState(false); const [verticalScale, setVerticalScale] = React.useState(1); - const [polylineEditingMode, setPolylineEditingMode] = React.useState(PolylineEditingMode.NONE); const [polylines, setPolylines] = React.useState([]); - /* - const { - onMouseEvent, - layers, - onDrag, - getCursor, - cursorPosition, - contextMenuItems, - activePolylineId, - polylines: changedPolylines, - } = useEditablePolylines({ - deckGlRef, - polylines, - editingMode: polylineEditingMode, - onEditingModeChange: handlePolylineEditingModeChange, - }); - - if (!isEqual(changedPolylines, polylines)) { - setPolylines(changedPolylines); - } - */ - function handleFitInViewClick() { setTriggerHomeCounter((prev) => prev + 1); } @@ -83,30 +60,10 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { setGridVisible(visible); } - function handlePolylineEditingModeChange(mode: PolylineEditingMode) { - polylinesPlugin.setEditingMode(mode); - } - - function handleMouseHover(event: MapMouseEvent): void { - setLayerPickingInfo(event.infos); - } - - function handleMouseEvent(event: MapMouseEvent): void { - if (event.type === "hover") { - handleMouseHover(event); - } - - // onMouseEvent(event); - } - function handleVerticalScaleChange(value: number) { setVerticalScale(value); } - function handleDrag(info: PickingInfo): void { - // onDrag(info); - } - const activePolylineId = polylinesPlugin.getCurrentEditingPolylineId(); const handlePolylineNameChange = React.useCallback( @@ -131,6 +88,11 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { [activePolylineId] ); + function handleMouseEvent(event: MapMouseEvent) { + const pickingInfo = event.infos; + setLayerPickingInfo(pickingInfo); + } + let adjustedLayers = [...props.layers]; if (!gridVisible) { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); @@ -143,50 +105,42 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { p.id === activePolylineId)?.name} /> - {/*cursorPosition && contextMenuItems.length && ( - - {contextMenuItems.map((item, index) => ( - - {item.icon} - {item.label} - - ))} - - )*/} setCameraPositionSetByAction(null)} - verticalScale={verticalScale} - scale={{ - visible: true, - incrementValue: 100, - widthPerUnit: 100, - cssStyle: { - right: 10, - top: 10, + {...deckGlManager.makeDeckGlComponentProps({ + deckGlRef: deckGlRef, + id: `subsurface-viewer-${id}`, + views: props.views, + cameraPosition: cameraPositionSetByAction ?? undefined, + onCameraPositionApplied: () => setCameraPositionSetByAction(null), + verticalScale, + scale: { + visible: true, + incrementValue: 100, + widthPerUnit: 100, + cssStyle: { + right: 10, + top: 10, + }, + }, + coords: { + visible: false, + multiPicking: true, + pickDepth: 2, }, - }} - coords={{ - visible: false, - multiPicking: true, - pickDepth: 2, - }} - triggerHome={triggerHomeCounter} - pickingRadius={5} - {...deckGlManager.makeDeckGlComponentProps(adjustedLayers)} + triggerHome: triggerHomeCounter, + pickingRadius: 5, + layers: adjustedLayers, + onMouseEvent: handleMouseEvent, + })} > {props.viewportAnnotations} diff --git a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx index a540fb000..b1f1c39fe 100644 --- a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx @@ -6,32 +6,39 @@ import { Input } from "@lib/components/Input"; import { ToggleButton } from "@lib/components/ToggleButton"; import { AddPathPointIcon, DrawPathIcon, RemovePathPointIcon } from "@lib/icons/"; import { Toolbar as GenericToolbar, ToolBarDivider } from "@modules/_shared/components/Toolbar"; +import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { Add, FilterCenterFocus, GridOff, GridOn, Polyline, Remove } from "@mui/icons-material"; import { PolylineEditingMode } from "../hooks/editablePolylines/types"; +import { PolylinesPlugin, PolylinesPluginTopic } from "../utils/PolylinesPlugin"; export type ToolbarProps = { verticalScale: number; - polylineEditingMode: PolylineEditingMode; hasActivePolyline: boolean; activePolylineName?: string; onFitInView: () => void; + polylinesPlugin: PolylinesPlugin; onGridVisibilityChange: (visible: boolean) => void; - onPolylineEditingModeChange: (mode: PolylineEditingMode) => void; onVerticalScaleChange(value: number): void; - onPolylineNameChange: (name: string) => void; + onPolylineNameChange(name: string): void; }; export function Toolbar(props: ToolbarProps): React.ReactNode { const [gridVisible, setGridVisible] = React.useState(false); - const [polylineEditingMode, setPolylineEditingMode] = React.useState(PolylineEditingMode.NONE); - const [prevPolylineEditingMode, setPrevPolylineEditingMode] = React.useState( - PolylineEditingMode.NONE + const [polylineName, setPolylineName] = React.useState(null); + const [prevEditingPolylineId, setPrevEditingPolylineId] = React.useState(null); + const polylineEditingMode = usePublishSubscribeTopicValue(props.polylinesPlugin, PolylinesPluginTopic.EDITING_MODE); + const editingPolylineId = usePublishSubscribeTopicValue( + props.polylinesPlugin, + PolylinesPluginTopic.EDITING_POLYLINE_ID ); - if (props.polylineEditingMode !== prevPolylineEditingMode) { - setPolylineEditingMode(props.polylineEditingMode); - setPrevPolylineEditingMode(props.polylineEditingMode); + if (editingPolylineId !== prevEditingPolylineId) { + setPrevEditingPolylineId(editingPolylineId); + const activePolyline = props.polylinesPlugin.getActivePolyline(); + if (activePolyline) { + setPolylineName(activePolyline.name); + } } function handleFitInViewClick() { @@ -53,21 +60,18 @@ export function Toolbar(props: ToolbarProps): React.ReactNode { function handleTogglePolylineEditing() { if (polylineEditingMode !== PolylineEditingMode.NONE) { - setPolylineEditingMode(PolylineEditingMode.NONE); - props.onPolylineEditingModeChange(PolylineEditingMode.NONE); + props.polylinesPlugin.setEditingMode(PolylineEditingMode.NONE); return; } - setPolylineEditingMode(PolylineEditingMode.IDLE); - props.onPolylineEditingModeChange(PolylineEditingMode.IDLE); + props.polylinesPlugin.setEditingMode(PolylineEditingMode.IDLE); } function handlePolylineEditingModeChange(mode: PolylineEditingMode) { - setPolylineEditingMode(mode); - props.onPolylineEditingModeChange(mode); + props.polylinesPlugin.setEditingMode(mode); } function handlePolylineNameChange(event: React.ChangeEvent) { - props.onPolylineNameChange(event.target.value); + props.polylinesPlugin.setActivePolylineName(event.target.value); } return ( @@ -144,8 +148,8 @@ export function Toolbar(props: ToolbarProps): React.ReactNode {
diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts index d5613e101..a7aca2816 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts @@ -79,6 +79,7 @@ export class PolylinesLayer extends CompositeLayer { parameters: { depthTest: false, }, + billboard: true, }) ); } @@ -97,6 +98,7 @@ export class PolylinesLayer extends CompositeLayer { parameters: { depthTest: false, }, + billboard: true, }) ); } @@ -133,7 +135,6 @@ export class PolylinesLayer extends CompositeLayer { getPath: (d) => d.path, getColor: (d: Polyline) => d.color, getWidth: 10, - billboard: false, widthUnits: "meters", widthMinPixels: 3, widthMaxPixels: 10, @@ -141,6 +142,7 @@ export class PolylinesLayer extends CompositeLayer { parameters: { depthTest: false, }, + billboard: true, }), new TextLayer({ id: `polylines-labels`, diff --git a/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts b/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts index 900807505..560794f61 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/DeckGlInstanceManager.ts @@ -5,11 +5,12 @@ import React from "react"; import { Layer, PickingInfo } from "@deck.gl/core"; import { DeckGLProps, DeckGLRef } from "@deck.gl/react"; +import { SubsurfaceViewerWithCameraStateProps } from "@modules/_shared/components/SubsurfaceViewerWithCameraState"; import { PublishSubscribe, PublishSubscribeDelegate } from "@modules_shared/utils/PublishSubscribeDelegate"; -import { MapMouseEvent, SubsurfaceViewerProps } from "@webviz/subsurface-viewer"; +import { MapMouseEvent } from "@webviz/subsurface-viewer"; export type ContextMenuItem = { - icon?: React.ReactNode; + icon?: React.ReactElement; label: string; onClick: () => void; }; @@ -18,6 +19,7 @@ export type ContextMenu = { position: { x: number; y: number }; items: ContextMenuItem[]; }; + export class DeckGlPlugin { private _manager: DeckGlInstanceManager; @@ -205,8 +207,10 @@ export class DeckGlInstanceManager implements PublishSubscribe[]): Partial { - const layers = [...withLayers]; + makeDeckGlComponentProps(props: SubsurfaceViewerWithCameraStateProps): SubsurfaceViewerWithCameraStateProps { + const layers = [...(props.layers ?? [])]; for (const plugin of this._plugins) { const pluginLayers = plugin.getLayers?.() ?? []; layers.push(...pluginLayers); @@ -300,8 +304,12 @@ export class DeckGlInstanceManager implements PublishSubscribe { + this.handleMouseEvent(event); + props.onMouseEvent?.(event); + }, getCursor: (state) => this.getCursor(state), layers, }; diff --git a/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.tsx similarity index 85% rename from frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts rename to frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.tsx index 0e249ac18..99f45a02a 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.tsx @@ -2,7 +2,8 @@ import addPathIcon from "@assets/add_path.svg"; import continuePathIcon from "@assets/continue_path.svg"; import removePathIcon from "@assets/remove_path.svg"; import { Layer, PickingInfo } from "@deck.gl/core"; -import { PublishSubscribeDelegate } from "@modules/_shared/utils/PublishSubscribeDelegate"; +import { PublishSubscribe, PublishSubscribeDelegate } from "@modules/_shared/utils/PublishSubscribeDelegate"; +import { Edit, Remove } from "@mui/icons-material"; import { v4 } from "uuid"; @@ -31,17 +32,13 @@ export enum PolylineEditingMode { } export enum PolylinesPluginTopic { - EDITING_POLYLINE_CHANGE = "editing_polyline_change", - EDITING_MODE_CHANGE = "editing_mode_change", + EDITING_POLYLINE_ID = "editing_polyline_id", + EDITING_MODE = "editing_mode_change", } export type PolylinesPluginTopicPayloads = { - [PolylinesPluginTopic.EDITING_MODE_CHANGE]: { - editingMode: PolylineEditingMode; - }; - [PolylinesPluginTopic.EDITING_POLYLINE_CHANGE]: { - editingPolylineId: string | null; - }; + [PolylinesPluginTopic.EDITING_MODE]: PolylineEditingMode; + [PolylinesPluginTopic.EDITING_POLYLINE_ID]: string | null; }; enum AppendToPathLocation { @@ -49,7 +46,7 @@ enum AppendToPathLocation { END = "end", } -export class PolylinesPlugin extends DeckGlPlugin { +export class PolylinesPlugin extends DeckGlPlugin implements PublishSubscribe { private _currentEditingPolylineId: string | null = null; private _currentEditingPolylinePathReferencePointIndex: number | null = null; private _polylines: Polyline[] = []; @@ -63,20 +60,42 @@ export class PolylinesPlugin extends DeckGlPlugin { private setCurrentEditingPolylineId(id: string | null): void { this._currentEditingPolylineId = id; - this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.EDITING_POLYLINE_CHANGE); + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.EDITING_POLYLINE_ID); } - private getActivePolyline(): Polyline | undefined { + getActivePolyline(): Polyline | undefined { return this._polylines.find((polyline) => polyline.id === this._currentEditingPolylineId); } + setActivePolylineName(name: string): void { + const activePolyline = this.getActivePolyline(); + if (!activePolyline) { + return; + } + + this._polylines = this._polylines.map((polyline) => { + if (polyline.id === activePolyline.id) { + return { + ...polyline, + name, + }; + } + return polyline; + }); + this.requireRedraw(); + } + + getPublishSubscribeDelegate(): PublishSubscribeDelegate { + return this._publishSubscribeDelegate; + } + setEditingMode(mode: PolylineEditingMode): void { this._editingMode = mode; if ([PolylineEditingMode.NONE, PolylineEditingMode.IDLE].includes(mode)) { this._hoverPoint = null; this._currentEditingPolylinePathReferencePointIndex = null; } - this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.EDITING_MODE_CHANGE); + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.EDITING_MODE); this.requireRedraw(); } @@ -101,6 +120,17 @@ export class PolylinesPlugin extends DeckGlPlugin { this._hoverPoint = null; this.setEditingMode(PolylineEditingMode.IDLE); this.requireRedraw(); + return; + } + if (key === "Delete") { + if (this._editingMode === PolylineEditingMode.IDLE) { + if (this._selectedPolylineId) { + this._polylines = this._polylines.filter((polyline) => polyline.id !== this._selectedPolylineId); + this._selectedPolylineId = null; + this.requireRedraw(); + } + return; + } } } @@ -303,12 +333,12 @@ export class PolylinesPlugin extends DeckGlPlugin { // An alternative would be to store a reference to the layer the polyline was first created upon // and always try to use that layer to get the coordinates const layerUnderCursor = this.getFirstLayerUnderCursorInfo(pickingInfo.x, pickingInfo.y); - if (!layerUnderCursor) { + if (!layerUnderCursor || !layerUnderCursor.coordinate) { return; } const newPath = [...activePolyline.path]; - newPath[this._draggedPathPointIndex] = [...pickingInfo.coordinate]; + newPath[this._draggedPathPointIndex] = [...layerUnderCursor.coordinate]; this.updateActivePolylinePath(newPath); this.requireRedraw(); } @@ -350,8 +380,8 @@ export class PolylinesPlugin extends DeckGlPlugin { return `url(${removePathIcon}) 4 2, crosshair`; } - if (this._editingMode === PolylineEditingMode.IDLE) { - return "pointer"; + if (this._editingMode === PolylineEditingMode.IDLE && pickingInfo.editableEntity.type === "point") { + return "grab"; } } @@ -369,6 +399,7 @@ export class PolylinesPlugin extends DeckGlPlugin { return [ { + icon: , label: "Edit", onClick: () => { this.setCurrentEditingPolylineId(pickingInfo.polylineId ?? null); @@ -376,6 +407,7 @@ export class PolylinesPlugin extends DeckGlPlugin { }, }, { + icon: , label: "Delete", onClick: () => { this._polylines = this._polylines.filter((polyline) => polyline.id !== pickingInfo.polylineId); @@ -429,18 +461,18 @@ export class PolylinesPlugin extends DeckGlPlugin { return layers; } - protected makeSnapshot(topic: T): PolylinesPluginTopicPayloads[T] { - if (topic === PolylinesPluginTopic.EDITING_MODE_CHANGE) { - return { - editingMode: this._editingMode, - } as PolylinesPluginTopicPayloads[T]; - } - if (topic === PolylinesPluginTopic.EDITING_POLYLINE_CHANGE) { - return { - editingPolylineId: this._currentEditingPolylineId, - } as PolylinesPluginTopicPayloads[T]; - } + makeSnapshotGetter(topic: T): () => PolylinesPluginTopicPayloads[T] { + const snapshotGetter = (): any => { + if (topic === PolylinesPluginTopic.EDITING_MODE) { + return this._editingMode; + } + if (topic === PolylinesPluginTopic.EDITING_POLYLINE_ID) { + return this._currentEditingPolylineId; + } + + throw new Error(`Unknown topic ${topic}`); + }; - throw new Error("Unknown topic"); + return snapshotGetter; } } From 77dcc03f0448a87687a32d54fe5109b88568b37a Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 30 Jan 2025 17:15:36 +0100 Subject: [PATCH 29/97] wip --- .../userCreatedItems/IntersectionPolylines.ts | 13 +- .../3DViewer/view/atoms/derivedAtoms.ts | 4 +- .../components/SubsurfaceViewerWrapper.tsx | 7 +- frontend/src/modules/3DViewer/view/view.tsx | 6 +- .../view/components/LayersWrapper.tsx | 6 + .../view/components/ReadoutWrapper.tsx | 73 ++- .../3DViewerNew/view/components/Toolbar.tsx | 8 +- .../deckGlLayers/EditablePolylineLayer.ts | 3 +- .../deckGlLayers/PolylinesLayer.ts | 3 +- .../editablePolylinesHook.tsx | 576 ------------------ .../view/hooks/editablePolylines/types.ts | 7 - .../view/utils/PolylinesPlugin.tsx | 66 +- .../src/modules/3DViewerNew/view/view.tsx | 2 + .../Intersection/view/atoms/derivedAtoms.ts | 8 +- 14 files changed, 151 insertions(+), 631 deletions(-) delete mode 100644 frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx diff --git a/frontend/src/framework/userCreatedItems/IntersectionPolylines.ts b/frontend/src/framework/userCreatedItems/IntersectionPolylines.ts index d7e5d86fe..eca15ad9e 100644 --- a/frontend/src/framework/userCreatedItems/IntersectionPolylines.ts +++ b/frontend/src/framework/userCreatedItems/IntersectionPolylines.ts @@ -2,13 +2,14 @@ import { AtomStoreMaster } from "@framework/AtomStoreMaster"; import { UserCreatedItemSet } from "@framework/UserCreatedItems"; import { atom } from "jotai"; -import { cloneDeep } from "lodash"; +import { cloneDeep, isEqual } from "lodash"; import { v4 } from "uuid"; export type IntersectionPolyline = { id: string; name: string; - points: number[][]; + color: [number, number, number]; + path: number[][]; }; export type IntersectionPolylineWithoutId = Omit; @@ -54,6 +55,14 @@ export class IntersectionPolylines implements UserCreatedItemSet { this.notifySubscribers(IntersectionPolylinesEvent.CHANGE); } + setPolylines(polylines: IntersectionPolyline[]): void { + if (isEqual(this._polylines, polylines)) { + return; + } + this._polylines = polylines; + this.notifySubscribers(IntersectionPolylinesEvent.CHANGE); + } + getPolylines(): IntersectionPolyline[] { return this._polylines; } diff --git a/frontend/src/modules/3DViewer/view/atoms/derivedAtoms.ts b/frontend/src/modules/3DViewer/view/atoms/derivedAtoms.ts index 5bf0c16c5..6086c4a48 100644 --- a/frontend/src/modules/3DViewer/view/atoms/derivedAtoms.ts +++ b/frontend/src/modules/3DViewer/view/atoms/derivedAtoms.ts @@ -42,11 +42,11 @@ export const intersectionReferenceSystemAtom = atom((get) => { return referenceSystem; } } else if (intersectionType === IntersectionType.CUSTOM_POLYLINE && customIntersectionPolyline) { - if (customIntersectionPolyline.points.length < 2) { + if (customIntersectionPolyline.path.length < 2) { return null; } const referenceSystem = new IntersectionReferenceSystem( - customIntersectionPolyline.points.map((point) => [point[0], point[1], 0]) + customIntersectionPolyline.path.map((point) => [point[0], point[1], 0]) ); referenceSystem.offset = 0; diff --git a/frontend/src/modules/3DViewer/view/components/SubsurfaceViewerWrapper.tsx b/frontend/src/modules/3DViewer/view/components/SubsurfaceViewerWrapper.tsx index ef43ea114..36b2b824f 100644 --- a/frontend/src/modules/3DViewer/view/components/SubsurfaceViewerWrapper.tsx +++ b/frontend/src/modules/3DViewer/view/components/SubsurfaceViewerWrapper.tsx @@ -114,7 +114,7 @@ export function SubsurfaceViewerWrapper(props: SubsurfaceViewerWrapperProps): Re if (!isEqual(props.intersectionPolyline, prevIntersectionPolyline)) { setPrevIntersectionPolyline(props.intersectionPolyline); if (props.intersectionPolyline) { - setCurrentlyEditedPolyline(props.intersectionPolyline.points); + setCurrentlyEditedPolyline(props.intersectionPolyline.path); setPolylineEditingActive(true); setPolylineEditPointsModusActive(true); setSelectedPolylinePointIndex(0); @@ -359,14 +359,15 @@ export function SubsurfaceViewerWrapper(props: SubsurfaceViewerWrapperProps): Re props.onIntersectionPolylineChange({ ...props.intersectionPolyline, name, - points: currentlyEditedPolyline, + path: currentlyEditedPolyline, }); } } else { if (props.onAddIntersectionPolyline && currentlyEditedPolyline.length > 1) { props.onAddIntersectionPolyline({ name, - points: currentlyEditedPolyline, + path: currentlyEditedPolyline, + color: [255, 0, 0], }); } handlePolylineEditingCancel(); diff --git a/frontend/src/modules/3DViewer/view/view.tsx b/frontend/src/modules/3DViewer/view/view.tsx index 8cf597a79..8cf9564cb 100644 --- a/frontend/src/modules/3DViewer/view/view.tsx +++ b/frontend/src/modules/3DViewer/view/view.tsx @@ -149,15 +149,15 @@ export function View(props: ModuleViewProps): React.ReactNode { } } } else if (intersectionType === IntersectionType.CUSTOM_POLYLINE) { - if (customIntersectionPolyline && customIntersectionPolyline.points.length >= 2) { + if (customIntersectionPolyline && customIntersectionPolyline.path.length >= 2) { intersectionReferenceSystem = new IntersectionReferenceSystem( - customIntersectionPolyline.points.map((point) => [point[0], point[1], 0]) + customIntersectionPolyline.path.map((point) => [point[0], point[1], 0]) ); intersectionReferenceSystem.offset = 0; if (!customIntersectionPolyline) { statusWriter.addError("Custom intersection polyline not found"); } else { - for (const point of customIntersectionPolyline.points) { + for (const point of customIntersectionPolyline.path) { polylineUtmXy.push(point[0], point[1]); } } diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 0b1047bd2..bb527063f 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -3,6 +3,8 @@ import React from "react"; import { View as DeckGlView } from "@deck.gl/core"; import { ViewContext } from "@framework/ModuleContext"; import { useViewStatusWriter } from "@framework/StatusWriter"; +import { WorkbenchSession } from "@framework/WorkbenchSession"; +import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { PendingWrapper } from "@lib/components/PendingWrapper"; import { useElementSize } from "@lib/hooks/useElementSize"; import { Rect3D, outerRectContainsInnerRect } from "@lib/utils/geometry"; @@ -26,6 +28,8 @@ export type LayersWrapperProps = { layerManager: LayerManager; preferredViewLayout: PreferredViewLayout; viewContext: ViewContext; + workbenchSession: WorkbenchSession; + workbenchSettings: WorkbenchSettings; }; export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { @@ -168,6 +172,8 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { viewportAnnotations={viewportAnnotations} layers={layers} bounds={bounds} + workbenchSession={props.workbenchSession} + workbenchSettings={props.workbenchSettings} />
diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index 3a1bcdce9..31ada2da3 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -2,24 +2,31 @@ import React from "react"; import { Layer as DeckGlLayer } from "@deck.gl/core"; import { DeckGLRef } from "@deck.gl/react"; +import { useIntersectionPolylines } from "@framework/UserCreatedItems"; +import { WorkbenchSession } from "@framework/WorkbenchSession"; +import { WorkbenchSettings } from "@framework/WorkbenchSettings"; +import { IntersectionPolylinesEvent } from "@framework/userCreatedItems/IntersectionPolylines"; import { SubsurfaceViewerWithCameraState } from "@modules/_shared/components/SubsurfaceViewerWithCameraState"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { BoundingBox3D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; +import culori from "culori"; + import { ContextMenu } from "./ContextMenu"; import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; import { Toolbar } from "./Toolbar"; -import { Polyline } from "../hooks/editablePolylines/types"; import { DeckGlInstanceManager, DeckGlInstanceManagerTopic } from "../utils/DeckGlInstanceManager"; -import { PolylinesPlugin } from "../utils/PolylinesPlugin"; +import { Polyline, PolylinesPlugin, PolylinesPluginTopic } from "../utils/PolylinesPlugin"; export type ReadooutWrapperProps = { views: ViewsType; viewportAnnotations: React.ReactNode[]; layers: DeckGlLayer[]; bounds?: BoundingBox3D; + workbenchSession: WorkbenchSession; + workbenchSettings: WorkbenchSettings; }; export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { @@ -32,18 +39,58 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { usePublishSubscribeTopicValue(deckGlManager, DeckGlInstanceManagerTopic.REDRAW); - React.useEffect(function setupDeckGlManager() { - const manager = new DeckGlInstanceManager(deckGlRef.current); - setDeckGlManager(manager); + const intersectionPolylines = useIntersectionPolylines(props.workbenchSession); + const colorSet = props.workbenchSettings.useColorSet(); + + const colorGenerator = React.useCallback( + function* colorGenerator() { + const colors: [number, number, number][] = colorSet.getColorArray().map((c) => { + const rgb = culori.converter("rgb")(c); + if (!rgb) { + return [0, 0, 0]; + } + return [rgb.r, rgb.g, rgb.b]; + }); + let i = 0; + while (true) { + yield colors[i % colors.length]; + i++; + } + }, + [colorSet] + ); - const polylinesPlugin = new PolylinesPlugin(manager); - manager.addPlugin(polylinesPlugin); - setPolylinesPlugin(polylinesPlugin); + React.useEffect( + function setupDeckGlManager() { + const manager = new DeckGlInstanceManager(deckGlRef.current); + setDeckGlManager(manager); + + const polylinesPlugin = new PolylinesPlugin(manager, colorGenerator()); + polylinesPlugin.setPolylines(intersectionPolylines.getPolylines()); + manager.addPlugin(polylinesPlugin); + setPolylinesPlugin(polylinesPlugin); + + const unsubscribeFromPolylinesPlugin = polylinesPlugin + .getPublishSubscribeDelegate() + .makeSubscriberFunction(PolylinesPluginTopic.POLYLINES)(() => { + intersectionPolylines.setPolylines(polylinesPlugin.getPolylines()); + }); + + const unsubscribeFromIntersectionPolylines = intersectionPolylines.subscribe( + IntersectionPolylinesEvent.CHANGE, + () => { + polylinesPlugin.setPolylines(intersectionPolylines.getPolylines()); + } + ); - return function cleanupDeckGlManager() { - manager.beforeDestroy(); - }; - }, []); + return function cleanupDeckGlManager() { + manager.beforeDestroy(); + unsubscribeFromPolylinesPlugin(); + unsubscribeFromIntersectionPolylines(); + }; + }, + [intersectionPolylines, colorGenerator] + ); const [cameraPositionSetByAction, setCameraPositionSetByAction] = React.useState(null); const [triggerHomeCounter, setTriggerHomeCounter] = React.useState(0); @@ -98,8 +145,6 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); } - // adjustedLayers.push(...layers); - return ( <> handlePolylineEditingModeChange( @@ -137,7 +137,7 @@ export function Toolbar(props: ToolbarProps): React.ReactNode { handlePolylineEditingModeChange( @@ -148,8 +148,8 @@ export function Toolbar(props: ToolbarProps): React.ReactNode {
diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts index c019455ba..0ab850d83 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/EditablePolylineLayer.ts @@ -1,11 +1,10 @@ import { CompositeLayer, GetPickingInfoParams, Layer, PickingInfo } from "@deck.gl/core"; import { PathStyleExtension } from "@deck.gl/extensions"; import { LineLayer, PathLayer, ScatterplotLayer } from "@deck.gl/layers"; +import { Polyline } from "@modules/3DViewerNew/view/utils/PolylinesPlugin"; import { AnimatedPathLayer } from "./AnimatedPathLayer"; -import { Polyline } from "../types"; - export enum AllowHoveringOf { NONE = "none", LINES = "line", diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts index a7aca2816..1aa0d48a8 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/deckGlLayers/PolylinesLayer.ts @@ -1,7 +1,6 @@ import { CompositeLayer, FilterContext, GetPickingInfoParams, Layer, PickingInfo } from "@deck.gl/core"; import { PathLayer, TextLayer } from "@deck.gl/layers"; - -import { Polyline } from "../types"; +import { Polyline } from "@modules/3DViewerNew/view/utils/PolylinesPlugin"; export type PolylinesLayerProps = { id: string; diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx deleted file mode 100644 index 894098eca..000000000 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/editablePolylinesHook.tsx +++ /dev/null @@ -1,576 +0,0 @@ -import React from "react"; - -import addPathIcon from "@assets/add_path.svg"; -import continuePathIcon from "@assets/continue_path.svg"; -import removePathIcon from "@assets/remove_path.svg"; -import setPathPointIcon from "@assets/set_path_point.svg"; -import { Layer, PickingInfo } from "@deck.gl/core"; -import { DeckGLProps, DeckGLRef } from "@deck.gl/react"; -import { Edit, Remove } from "@mui/icons-material"; -import { MapMouseEvent } from "@webviz/subsurface-viewer"; - -import { isEqual } from "lodash"; -import { v4 } from "uuid"; - -import { - AllowHoveringOf, - EditablePolylineLayer, - isEditablePolylineLayerPickingInfo, -} from "./deckGlLayers/EditablePolylineLayer"; -import { PolylinesLayer, isPolylinesLayerPickingInfo } from "./deckGlLayers/PolylinesLayer"; -import { ContextMenuItem, Polyline, PolylineEditingMode } from "./types"; - -export type UseEditablePolylinesProps = { - deckGlRef: React.MutableRefObject; - editingMode: PolylineEditingMode; - polylines: Polyline[]; - onEditingModeChange?: (mode: PolylineEditingMode) => void; -}; - -export type UseEditablePolylinesReturnType = { - layers: Layer[]; - onDrag: (info: PickingInfo) => boolean; - onMouseEvent: (event: MapMouseEvent) => void; - disableCameraInteraction: boolean; - getCursor: DeckGLProps["getCursor"]; - cursorPosition: number[] | null; - contextMenuItems: ContextMenuItem[]; - activePolylineId: string | null; - polylines: Polyline[]; -}; - -enum CursorIcon { - NONE = "none", - POINTER = "pointer", - ADD_POINT = "add-point", - REMOVE_POINT = "remove-point", - CONTINUE_FROM_POINT = "continue-from-point", -} - -enum PathAppendLocation { - START = "start", - END = "end", -} - -export function useEditablePolylines(props: UseEditablePolylinesProps): UseEditablePolylinesReturnType { - const { onEditingModeChange } = props; - - const [hoverPoint, setHoverPoint] = React.useState(null); - const [activePolylineId, setActivePolylineId] = React.useState(null); - const [polylines, setPolylines] = React.useState(props.polylines); - const [prevPolylines, setPrevPolylines] = React.useState(props.polylines); - const [cursorIcon, setCursorIcon] = React.useState(null); - const [cursorPosition, setCursorPosition] = React.useState(null); - const [appendLocation, setAppendLocation] = React.useState(PathAppendLocation.END); - const [draggedPointIndex, setDraggedPointIndex] = React.useState(null); - const [contextMenuItems, setContextMenuItems] = React.useState([]); - const [selectedPolylineId, setSelectedPolylineId] = React.useState(null); - const [referencePathPointIndex, setReferencePathPointIndex] = React.useState(undefined); - const [prevEditingMode, setPrevEditingMode] = React.useState(PolylineEditingMode.NONE); - - let changedPolylines = polylines; - - if (!isEqual(props.polylines, prevPolylines)) { - setPolylines(props.polylines); - setPrevPolylines(props.polylines); - changedPolylines = props.polylines; - } - - if (props.editingMode !== prevEditingMode) { - if (props.editingMode === PolylineEditingMode.NONE) { - setActivePolylineId(null); - setReferencePathPointIndex(undefined); - } - if (props.editingMode === PolylineEditingMode.IDLE) { - setReferencePathPointIndex(undefined); - } - setPrevEditingMode(props.editingMode); - } - - React.useEffect( - function onMount() { - function onKeyUp(event: KeyboardEvent) { - if (event.key === "Escape") { - setReferencePathPointIndex(undefined); - onEditingModeChange?.(PolylineEditingMode.IDLE); - } - } - - window.addEventListener("keyup", onKeyUp); - - return () => { - window.removeEventListener("keyup", onKeyUp); - }; - }, - [onEditingModeChange] - ); - - const onMouseEvent = React.useCallback( - (event: MapMouseEvent) => { - if (draggedPointIndex !== null) { - return; - } - - const activePolyline = polylines.find((polyline) => polyline.id === activePolylineId); - - if (event.type === "hover") { - if (!event.x || !event.y) { - setHoverPoint(null); - setCursorIcon(CursorIcon.NONE); - return; - } - - const firstLayerInfos = event.infos[0]; - - if (!firstLayerInfos) { - setHoverPoint(null); - setCursorIcon(CursorIcon.NONE); - return; - } - - const editablePolylineLayerPickingInfo = event.infos.find(isEditablePolylineLayerPickingInfo); - - if ( - editablePolylineLayerPickingInfo && - editablePolylineLayerPickingInfo.viewport && - firstLayerInfos.coordinate - ) { - if ( - [PolylineEditingMode.DRAW, PolylineEditingMode.ADD_POINT].includes(props.editingMode) && - editablePolylineLayerPickingInfo.editableEntity && - editablePolylineLayerPickingInfo.editableEntity.type === "line" - ) { - setCursorIcon(CursorIcon.ADD_POINT); - return; - } - if ( - [PolylineEditingMode.DRAW, PolylineEditingMode.REMOVE_POINT].includes(props.editingMode) && - activePolyline && - editablePolylineLayerPickingInfo.editableEntity && - editablePolylineLayerPickingInfo.editableEntity.type === "point" - ) { - const index = editablePolylineLayerPickingInfo.editableEntity.index; - if ( - (index === 0 || index === activePolyline.path.length - 1) && - index !== referencePathPointIndex - ) { - setCursorIcon(CursorIcon.CONTINUE_FROM_POINT); - return; - } - - setCursorIcon(CursorIcon.REMOVE_POINT); - return; - } - } - - const polylinesLayerPickingInfo = event.infos.find(isPolylinesLayerPickingInfo); - - if (polylinesLayerPickingInfo && props.editingMode === PolylineEditingMode.IDLE) { - setCursorIcon(CursorIcon.POINTER); - return; - } - - setCursorIcon(CursorIcon.NONE); - - if (firstLayerInfos && firstLayerInfos.coordinate) { - setHoverPoint([...firstLayerInfos.coordinate]); - } - } - - if (event.type === "click") { - const firstLayerInfos = event.infos[0]; - if (!firstLayerInfos || !firstLayerInfos.coordinate) { - setContextMenuItems([]); - setSelectedPolylineId(null); - return; - } - - if (props.editingMode === PolylineEditingMode.IDLE && !activePolylineId) { - const polylinesLayerPickingInfo = event.infos.find(isPolylinesLayerPickingInfo); - if ( - !polylinesLayerPickingInfo || - !polylinesLayerPickingInfo.viewport || - !polylinesLayerPickingInfo.coordinate - ) { - setContextMenuItems([]); - setSelectedPolylineId(null); - return; - } - - const position = polylinesLayerPickingInfo.viewport.project([ - ...polylinesLayerPickingInfo.coordinate, - ]); - setCursorPosition(position); - setSelectedPolylineId(polylinesLayerPickingInfo.polylineId ?? null); - - setContextMenuItems([ - { - icon: , - label: "Edit", - onClick: () => { - setActivePolylineId(polylinesLayerPickingInfo.polylineId ?? null); - setReferencePathPointIndex(undefined); - onEditingModeChange?.(PolylineEditingMode.DRAW); - setSelectedPolylineId(null); - setContextMenuItems([]); - }, - }, - { - icon: , - label: "Delete", - onClick: () => { - setPolylines((prevPolylines) => - prevPolylines.filter( - (polyline) => polyline.id !== polylinesLayerPickingInfo.polylineId - ) - ); - setContextMenuItems([]); - setSelectedPolylineId(null); - }, - }, - ]); - - return; - } - - setContextMenuItems([]); - - const editablePolylineLayerPickingInfo = event.infos.find(isEditablePolylineLayerPickingInfo); - if ( - activePolyline && - editablePolylineLayerPickingInfo && - editablePolylineLayerPickingInfo.editableEntity - ) { - // Remove point - if ( - editablePolylineLayerPickingInfo.editableEntity.type === "point" && - [PolylineEditingMode.DRAW, PolylineEditingMode.REMOVE_POINT].includes(props.editingMode) - ) { - const index = editablePolylineLayerPickingInfo.editableEntity.index; - - if ( - (index === 0 || index === activePolyline.path.length - 1) && - index !== referencePathPointIndex - ) { - setReferencePathPointIndex(index); - if (index === 0) { - setAppendLocation(PathAppendLocation.START); - } else { - setAppendLocation(PathAppendLocation.END); - } - return; - } - - let newReferencePathPointIndex: number | undefined = undefined; - if (referencePathPointIndex !== undefined) { - newReferencePathPointIndex = Math.max(0, referencePathPointIndex - 1); - if (editablePolylineLayerPickingInfo.editableEntity.index > referencePathPointIndex) { - newReferencePathPointIndex = referencePathPointIndex; - } - if (activePolyline.path.length - 1 < 1) { - newReferencePathPointIndex = undefined; - } - } - const newPolyline: Polyline = { - ...activePolyline, - path: activePolyline.path.filter( - (_, index) => index !== editablePolylineLayerPickingInfo.editableEntity?.index - ), - }; - - setReferencePathPointIndex(newReferencePathPointIndex); - - setPolylines((prevPolylines) => - prevPolylines.map((polyline) => - polyline.id === activePolyline.id ? newPolyline : polyline - ) - ); - setCursorIcon(CursorIcon.NONE); - - return; - } - if ( - editablePolylineLayerPickingInfo.editableEntity.type === "line" && - [PolylineEditingMode.DRAW, PolylineEditingMode.ADD_POINT].includes(props.editingMode) - ) { - const newPath = [...activePolyline.path]; - newPath.splice(editablePolylineLayerPickingInfo.editableEntity.index + 1, 0, [ - ...firstLayerInfos.coordinate, - ]); - - let newReferencePathPointIndex: number | undefined = undefined; - if (referencePathPointIndex !== undefined) { - newReferencePathPointIndex = referencePathPointIndex + 1; - if (editablePolylineLayerPickingInfo.editableEntity.index > referencePathPointIndex) { - newReferencePathPointIndex = referencePathPointIndex; - } - } - - const newPolyline: Polyline = { - ...activePolyline, - path: newPath, - }; - - setReferencePathPointIndex(newReferencePathPointIndex); - - setPolylines((prevPolylines) => - prevPolylines.map((polyline) => - polyline.id === activePolyline.id ? newPolyline : polyline - ) - ); - setCursorIcon(CursorIcon.NONE); - return; - } - } - - if (!activePolyline && props.editingMode === PolylineEditingMode.DRAW) { - const id = v4(); - const newPolyline: Polyline = { - id, - name: "New polyline", - color: [230, 136, 21, 255], - path: [[...firstLayerInfos.coordinate]], - }; - setPolylines([...polylines, newPolyline]); - setActivePolylineId(id); - setReferencePathPointIndex(0); - } else if (activePolyline) { - if (referencePathPointIndex === undefined) { - setActivePolylineId(null); - onEditingModeChange?.(PolylineEditingMode.IDLE); - return; - } - if (props.editingMode === PolylineEditingMode.DRAW) { - const newPolyline: Polyline = { - ...activePolyline, - path: appendCoordinateToPath( - activePolyline.path, - firstLayerInfos.coordinate, - appendLocation - ), - }; - setPolylines((prevPolylines) => - prevPolylines.map((polyline) => - polyline.id === activePolyline.id ? newPolyline : polyline - ) - ); - - setReferencePathPointIndex( - appendLocation === PathAppendLocation.END ? activePolyline.path.length : 0 - ); - } - } - } - }, - [ - props.editingMode, - activePolylineId, - appendLocation, - polylines, - referencePathPointIndex, - draggedPointIndex, - onEditingModeChange, - ] - ); - - const disablePanning = React.useCallback( - function disablePanning() { - if (props.deckGlRef.current?.deck) { - props.deckGlRef.current.deck.setProps({ - controller: { - dragRotate: false, - dragPan: false, - }, - }); - } - }, - [props.deckGlRef] - ); - - const enablePanning = React.useCallback( - function enablePanning() { - if (props.deckGlRef.current?.deck) { - props.deckGlRef.current.deck.setProps({ - controller: { - dragRotate: true, - dragPan: true, - }, - }); - } - }, - [props.deckGlRef] - ); - - const onDragStart = React.useCallback( - function onDragStart(info: PickingInfo) { - if (!isEditablePolylineLayerPickingInfo(info)) { - return false; - } - - if (info.editableEntity?.type === "point") { - setDraggedPointIndex(info.editableEntity.index); - disablePanning(); - return true; - } - }, - [disablePanning] - ); - - const onDrag = React.useCallback( - function onDrag(info: PickingInfo) { - if (draggedPointIndex === null || !activePolylineId || !info.coordinate) { - return false; - } - - if (!props.deckGlRef.current) { - return false; - } - - const layers = props.deckGlRef.current.pickMultipleObjects({ - x: info.x, - y: info.y, - radius: 10, - depth: 1, - unproject3D: true, - }); - - if (!layers.length) { - return false; - } - - const firstLayerInfos = layers[0]; - - if (!firstLayerInfos || !firstLayerInfos.coordinate) { - return false; - } - - setPolylines((prevPolylines) => { - const activePolyline = prevPolylines.find((polyline) => polyline.id === activePolylineId); - if (!activePolyline || !firstLayerInfos.coordinate) { - return prevPolylines; - } - - const newPath = [...activePolyline.path]; - newPath[draggedPointIndex] = [...firstLayerInfos.coordinate]; - - const newPolyline: Polyline = { - ...activePolyline, - path: newPath, - }; - return prevPolylines.map((polyline) => (polyline.id === activePolyline.id ? newPolyline : polyline)); - }); - - return false; - }, - [draggedPointIndex, activePolylineId, props.deckGlRef] - ); - - const onDragEnd = React.useCallback( - function onDragEnd() { - setDraggedPointIndex(null); - enablePanning(); - }, - [enablePanning] - ); - - const layers: Layer[] = [ - new PolylinesLayer({ - id: "polylines-layer", - polylines: polylines.filter((polyline) => polyline.id !== activePolylineId), - selectedPolylineId: - props.editingMode === PolylineEditingMode.NONE ? undefined : selectedPolylineId ?? undefined, - hoverable: props.editingMode === PolylineEditingMode.IDLE, - }), - ]; - - let allowHoveringOf = AllowHoveringOf.NONE; - if (props.editingMode === PolylineEditingMode.DRAW) { - allowHoveringOf = AllowHoveringOf.LINES_AND_POINTS; - } - if (props.editingMode === PolylineEditingMode.ADD_POINT) { - allowHoveringOf = AllowHoveringOf.LINES; - } - if (props.editingMode === PolylineEditingMode.REMOVE_POINT) { - allowHoveringOf = AllowHoveringOf.POINTS; - } - - if (activePolylineId) { - const activePolyline = polylines.find((polyline) => polyline.id === activePolylineId); - layers.push( - new EditablePolylineLayer({ - id: "editable-polylines-layer", - polyline: activePolyline, - mouseHoverPoint: hoverPoint ?? undefined, - referencePathPointIndex: - props.editingMode === PolylineEditingMode.DRAW ? referencePathPointIndex : undefined, - onDragStart, - onDragEnd, - allowHoveringOf, - }) - ); - } - - const getCursor = React.useCallback( - function getCursor(cursorState: Parameters>[0]): string { - if (cursorState.isDragging) { - return "grabbing"; - } - - if (cursorIcon === CursorIcon.POINTER) { - return "pointer"; - } - - if (cursorIcon === CursorIcon.CONTINUE_FROM_POINT) { - return `url(${continuePathIcon}) 4 2, crosshair`; - } - - if (cursorIcon === CursorIcon.ADD_POINT) { - return `url(${addPathIcon}) 4 2, crosshair`; - } - - if (cursorIcon === CursorIcon.REMOVE_POINT) { - return `url(${removePathIcon}) 4 2, crosshair`; - } - - if ( - (activePolylineId && referencePathPointIndex !== undefined) || - (!activePolylineId && props.editingMode === PolylineEditingMode.DRAW) - ) { - return `url(${setPathPointIcon}) 4 2, crosshair`; - } - - return "default"; - }, - [cursorIcon, activePolylineId, referencePathPointIndex, props.editingMode] - ); - - return { - layers, - onMouseEvent, - onDrag, - getCursor, - disableCameraInteraction: draggedPointIndex !== null, - contextMenuItems, - cursorPosition, - activePolylineId, - polylines: changedPolylines, - }; -} - -function appendCoordinateToPath(path: number[][], coordinate: number[], index: number): number[][]; -function appendCoordinateToPath(path: number[][], coordinate: number[], appendLocation: PathAppendLocation): number[][]; -function appendCoordinateToPath( - path: number[][], - coordinate: number[], - indexOrAppendLocation: number | PathAppendLocation -): number[][] { - if (typeof indexOrAppendLocation === "number") { - return [...path.slice(0, indexOrAppendLocation), coordinate, ...path.slice(indexOrAppendLocation)]; - } - - if (indexOrAppendLocation === PathAppendLocation.START) { - return [coordinate, ...path]; - } - - return [...path, coordinate]; -} diff --git a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts index cf01cf7d2..58f16acde 100644 --- a/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts +++ b/frontend/src/modules/3DViewerNew/view/hooks/editablePolylines/types.ts @@ -1,12 +1,5 @@ import React from "react"; -export type Polyline = { - id: string; - name: string; - color: [number, number, number, number]; - path: number[][]; -}; - export enum PolylineEditingMode { DRAW = "draw", ADD_POINT = "add_point", diff --git a/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.tsx b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.tsx index 99f45a02a..cfab80d8d 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.tsx +++ b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.tsx @@ -5,9 +5,10 @@ import { Layer, PickingInfo } from "@deck.gl/core"; import { PublishSubscribe, PublishSubscribeDelegate } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { Edit, Remove } from "@mui/icons-material"; +import { isEqual } from "lodash"; import { v4 } from "uuid"; -import { ContextMenuItem, DeckGlPlugin } from "./DeckGlInstanceManager"; +import { ContextMenuItem, DeckGlInstanceManager, DeckGlPlugin } from "./DeckGlInstanceManager"; import { AllowHoveringOf, @@ -19,7 +20,7 @@ import { PolylinesLayer, isPolylinesLayerPickingInfo } from "../hooks/editablePo export type Polyline = { id: string; name: string; - color: [number, number, number, number]; + color: [number, number, number]; path: number[][]; }; @@ -33,12 +34,14 @@ export enum PolylineEditingMode { export enum PolylinesPluginTopic { EDITING_POLYLINE_ID = "editing_polyline_id", - EDITING_MODE = "editing_mode_change", + EDITING_MODE = "editing_mode", + POLYLINES = "polylines", } export type PolylinesPluginTopicPayloads = { [PolylinesPluginTopic.EDITING_MODE]: PolylineEditingMode; [PolylinesPluginTopic.EDITING_POLYLINE_ID]: string | null; + [PolylinesPluginTopic.POLYLINES]: Polyline[]; }; enum AppendToPathLocation { @@ -46,6 +49,23 @@ enum AppendToPathLocation { END = "end", } +function* defaultColorGenerator() { + const colors: [number, number, number][] = [ + [255, 0, 0], + [0, 255, 0], + [0, 0, 255], + [255, 255, 0], + [255, 0, 255], + [0, 255, 255], + ]; + + let index = 0; + while (true) { + yield colors[index]; + index = (index + 1) % colors.length; + } +} + export class PolylinesPlugin extends DeckGlPlugin implements PublishSubscribe { private _currentEditingPolylineId: string | null = null; private _currentEditingPolylinePathReferencePointIndex: number | null = null; @@ -55,6 +75,7 @@ export class PolylinesPlugin extends DeckGlPlugin implements PublishSubscribe; private _publishSubscribeDelegate = new PublishSubscribeDelegate(); @@ -63,10 +84,28 @@ export class PolylinesPlugin extends DeckGlPlugin implements PublishSubscribe) { + super(manager); + this._colorGenerator = colorGenerator ?? defaultColorGenerator(); + } + getActivePolyline(): Polyline | undefined { return this._polylines.find((polyline) => polyline.id === this._currentEditingPolylineId); } + getPolylines(): Polyline[] { + return this._polylines; + } + + setPolylines(polylines: Polyline[]): void { + if (isEqual(this._polylines, polylines)) { + return; + } + this._polylines = polylines; + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.POLYLINES); + this.requireRedraw(); + } + setActivePolylineName(name: string): void { const activePolyline = this.getActivePolyline(); if (!activePolyline) { @@ -82,6 +121,7 @@ export class PolylinesPlugin extends DeckGlPlugin implements PublishSubscribe { this._polylines = this._polylines.filter((polyline) => polyline.id !== pickingInfo.polylineId); this.setCurrentEditingPolylineId(null); + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.POLYLINES); this.requireRedraw(); }, }, @@ -430,12 +472,9 @@ export class PolylinesPlugin extends DeckGlPlugin implements PublishSubscribe): React.ReactNode { layerManager={layerManager} preferredViewLayout={preferredViewLayout} viewContext={props.viewContext} + workbenchSession={props.workbenchSession} + workbenchSettings={props.workbenchSettings} /> ); } diff --git a/frontend/src/modules/Intersection/view/atoms/derivedAtoms.ts b/frontend/src/modules/Intersection/view/atoms/derivedAtoms.ts index 2e19c5dd6..935501273 100644 --- a/frontend/src/modules/Intersection/view/atoms/derivedAtoms.ts +++ b/frontend/src/modules/Intersection/view/atoms/derivedAtoms.ts @@ -49,11 +49,11 @@ export const intersectionReferenceSystemAtom = atom((get) => { return referenceSystem; } } else if (intersectionType === IntersectionType.CUSTOM_POLYLINE && customIntersectionPolyline) { - if (customIntersectionPolyline.points.length < 2) { + if (customIntersectionPolyline.path.length < 2) { return null; } const referenceSystem = new IntersectionReferenceSystem( - customIntersectionPolyline.points.map((point) => [point[0], point[1], 0]) + customIntersectionPolyline.path.map((point) => [point[0], point[1], 0]) ); referenceSystem.offset = 0; @@ -83,10 +83,10 @@ export const polylineAtom = atom((get) => { polylineUtmXy.push(...simplifiedCurveResult.simplifiedWellboreTrajectoryXy.flat()); actualSectionLengths.push(...simplifiedCurveResult.actualSectionLengths); } else if (intersectionType === IntersectionType.CUSTOM_POLYLINE && selectedCustomIntersectionPolyline) { - for (const [index, point] of selectedCustomIntersectionPolyline.points.entries()) { + for (const [index, point] of selectedCustomIntersectionPolyline.path.entries()) { polylineUtmXy.push(point[0], point[1]); if (index > 0) { - const previousPoint = selectedCustomIntersectionPolyline.points[index - 1]; + const previousPoint = selectedCustomIntersectionPolyline.path[index - 1]; actualSectionLengths.push(point2Distance(vec2FromArray(point), vec2FromArray(previousPoint))); } } From 3e249bdd544a45e2d5836881ecee7a37dc30b6c3 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 31 Jan 2025 17:05:35 +0100 Subject: [PATCH 30/97] wip --- .../RealizationGridLayer.ts | 241 ++++++++++++++++-- .../RealizationGridSettingsContext.ts | 56 +++- .../RealizationGridLayer/types.ts | 1 + .../view/components/ReadoutWrapper.tsx | 6 +- .../3DViewerNew/view/utils/layerFactory.ts | 105 ++++++++ .../framework/LayerManager/LayerManager.ts | 14 + .../implementations/IntersectionSetting.tsx | 112 ++++++++ .../LayerFramework/settings/settingsTypes.ts | 1 + 8 files changed, 507 insertions(+), 29 deletions(-) create mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/IntersectionSetting.tsx diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts index af3089c10..55df970ed 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -1,4 +1,11 @@ -import { getGridParameterOptions, getGridSurfaceOptions } from "@api"; +import { + getGridParameterOptions, + getGridSurfaceOptions, + getWellTrajectoriesOptions, + postGetPolylineIntersectionOptions, +} from "@api"; +import { IntersectionReferenceSystem } from "@equinor/esv-intersection"; +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; import { GridMappedProperty_trans, GridSurface_trans, @@ -11,6 +18,11 @@ import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerMan import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; +import { + PolylineIntersection_trans, + calcExtendedSimplifiedWellboreTrajectoryInXYPlane, + transformPolylineIntersection, +} from "@modules/_shared/utils/wellbore"; import { QueryClient } from "@tanstack/react-query"; import { isEqual } from "lodash"; @@ -23,16 +35,18 @@ export class RealizationGridLayer Layer< RealizationGridSettings, { - gridSurfaceData: GridSurface_trans; - gridParameterData: GridMappedProperty_trans; + gridSurfaceData: GridSurface_trans | null; + gridParameterData: GridMappedProperty_trans | null; + polylineIntersectionData: PolylineIntersection_trans | null; } > { private _layerDelegate: LayerDelegate< RealizationGridSettings, { - gridSurfaceData: GridSurface_trans; - gridParameterData: GridMappedProperty_trans; + gridSurfaceData: GridSurface_trans | null; + gridParameterData: GridMappedProperty_trans | null; + polylineIntersectionData: PolylineIntersection_trans | null; } >; private _itemDelegate: ItemDelegate; @@ -58,8 +72,9 @@ export class RealizationGridLayer getLayerDelegate(): LayerDelegate< RealizationGridSettings, { - gridSurfaceData: GridSurface_trans; - gridParameterData: GridMappedProperty_trans; + gridSurfaceData: GridSurface_trans | null; + gridParameterData: GridMappedProperty_trans | null; + polylineIntersectionData: PolylineIntersection_trans | null; } > { return this._layerDelegate; @@ -78,17 +93,25 @@ export class RealizationGridLayer return null; } - return { - x: [ - data.gridSurfaceData.origin_utm_x + data.gridSurfaceData.xmin, - data.gridSurfaceData.origin_utm_x + data.gridSurfaceData.xmax, - ], - y: [ - data.gridSurfaceData.origin_utm_y + data.gridSurfaceData.ymin, - data.gridSurfaceData.origin_utm_y + data.gridSurfaceData.ymax, - ], - z: [data.gridSurfaceData.zmin, data.gridSurfaceData.zmax], - }; + if (data.polylineIntersectionData) { + return null; + } + + if (data.gridSurfaceData) { + return { + x: [ + data.gridSurfaceData.origin_utm_x + data.gridSurfaceData.xmin, + data.gridSurfaceData.origin_utm_x + data.gridSurfaceData.xmax, + ], + y: [ + data.gridSurfaceData.origin_utm_y + data.gridSurfaceData.ymin, + data.gridSurfaceData.origin_utm_y + data.gridSurfaceData.ymax, + ], + z: [data.gridSurfaceData.zmin, data.gridSurfaceData.zmax], + }; + } + + return null; } makeValueRange(): [number, number] | null { @@ -97,16 +120,29 @@ export class RealizationGridLayer return null; } - return [data.gridParameterData.min_grid_prop_value, data.gridParameterData.max_grid_prop_value]; + if (data.polylineIntersectionData) { + return [ + data.polylineIntersectionData.min_grid_prop_value, + data.polylineIntersectionData.max_grid_prop_value, + ]; + } + + if (data.gridParameterData) { + return [data.gridParameterData.min_grid_prop_value, data.gridParameterData.max_grid_prop_value]; + } + + return null; } fetchData(queryClient: QueryClient): Promise<{ - gridSurfaceData: GridSurface_trans; - gridParameterData: GridMappedProperty_trans; + gridSurfaceData: GridSurface_trans | null; + gridParameterData: GridMappedProperty_trans | null; + polylineIntersectionData: PolylineIntersection_trans | null; }> { const settings = this.getSettingsContext().getDelegate().getSettings(); const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); + const intersection = settings[SettingType.INTERSECTION].getDelegate().getValue(); const gridName = settings[SettingType.GRID_NAME].getDelegate().getValue(); const attribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); @@ -129,13 +165,167 @@ export class RealizationGridLayer const kMin = layerKRange?.[0] ?? 0; const kMax = layerKRange?.[1] ?? 0; - const queryKey = [ - "gridParameter", + if (!intersection) { + return this.fetchGridParameterAndSurface( + queryClient, + ensembleIdent, + gridName, + attribute, + timeOrInterval, + realizationNum, + iMin, + iMax, + jMin, + jMax, + kMin, + kMax + ); + } + + return this.fetchPolylineIntersection( + queryClient, ensembleIdent, gridName, attribute, timeOrInterval, realizationNum, + intersection + ); + } + + private fetchPolylineIntersection( + queryClient: QueryClient, + ensembleIdent: RegularEnsembleIdent | null, + gridName: string | null, + parameterName: string | null, + timeOrInterval: string | null, + realizationNum: number | null, + intersection: { type: "wellbore" | "polyline"; name: string; uuid: string } + ): Promise<{ + gridSurfaceData: GridSurface_trans | null; + gridParameterData: GridMappedProperty_trans | null; + polylineIntersectionData: PolylineIntersection_trans | null; + }> { + const fieldIdentifier = this._itemDelegate.getLayerManager().getGlobalSetting("fieldId"); + + const queryKey = [ + "gridIntersection", + ensembleIdent, + gridName, + parameterName, + timeOrInterval, + realizationNum, + intersection, + ]; + this._layerDelegate.registerQueryKey(queryKey); + + let makePolylinePromise: Promise = new Promise((resolve) => { + if (intersection.type === "wellbore") { + return queryClient + .fetchQuery({ + ...getWellTrajectoriesOptions({ + query: { + field_identifier: fieldIdentifier ?? "", + wellbore_uuids: [intersection.uuid], + }, + }), + }) + .then((data) => { + const path: number[][] = []; + for (const [index, northing] of data[0].northingArr.entries()) { + const easting = data[0].eastingArr[index]; + const tvd_msl = data[0].tvdMslArr[index]; + + path.push([easting, northing, tvd_msl]); + } + const offset = data[0].tvdMslArr[0]; + + const intersectionReferenceSystem = new IntersectionReferenceSystem(path); + intersectionReferenceSystem.offset = offset; + + const polylineUtmXy: number[] = []; + polylineUtmXy.push( + ...calcExtendedSimplifiedWellboreTrajectoryInXYPlane( + path, + 0, + 5 + ).simplifiedWellboreTrajectoryXy.flat() + ); + + resolve(polylineUtmXy); + }); + } else { + const intersectionPolyline = this._itemDelegate + .getLayerManager() + .getWorkbenchSession() + .getUserCreatedItems() + .getIntersectionPolylines() + .getPolyline(intersection.uuid); + if (!intersectionPolyline) { + resolve([]); + return; + } + + const polylineUtmXy: number[] = []; + for (const point of intersectionPolyline.path) { + polylineUtmXy.push(point[0], point[1]); + } + + resolve(polylineUtmXy); + } + }); + + const gridIntersectionPromise = makePolylinePromise + .then((polyline_utm_xy) => + queryClient.fetchQuery({ + ...postGetPolylineIntersectionOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + grid_name: gridName ?? "", + parameter_name: parameterName ?? "", + parameter_time_or_interval_str: timeOrInterval, + realization_num: realizationNum ?? 0, + }, + body: { polyline_utm_xy }, + }), + }) + ) + .then(transformPolylineIntersection) + .then((data) => ({ + polylineIntersectionData: data, + gridSurfaceData: null, + gridParameterData: null, + })); + + return gridIntersectionPromise; + } + + private fetchGridParameterAndSurface( + queryClient: QueryClient, + ensembleIdent: RegularEnsembleIdent | null, + gridName: string | null, + parameterName: string | null, + parameterTimeOrInterval: string | null, + realizationNum: number | null, + iMin: number, + iMax: number, + jMin: number, + jMax: number, + kMin: number, + kMax: number + ): Promise<{ + gridSurfaceData: GridSurface_trans | null; + gridParameterData: GridMappedProperty_trans | null; + polylineIntersectionData: PolylineIntersection_trans | null; + }> { + const queryKey = [ + "gridParameter", + ensembleIdent, + gridName, + parameterName, + parameterTimeOrInterval, + realizationNum, iMin, iMax, jMin, @@ -152,8 +342,8 @@ export class RealizationGridLayer case_uuid: ensembleIdent?.getCaseUuid() ?? "", ensemble_name: ensembleIdent?.getEnsembleName() ?? "", grid_name: gridName ?? "", - parameter_name: attribute ?? "", - parameter_time_or_interval_str: timeOrInterval, + parameter_name: parameterName ?? "", + parameter_time_or_interval_str: parameterTimeOrInterval, realization_num: realizationNum ?? 0, i_min: iMin, i_max: iMax - 1, @@ -188,6 +378,7 @@ export class RealizationGridLayer return Promise.all([gridSurfacePromise, gridParameterPromise]).then(([gridSurfaceData, gridParameterData]) => ({ gridSurfaceData, gridParameterData, + polylineIntersectionData: null, })); } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts index eef22bb55..8f34f5442 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts @@ -1,4 +1,4 @@ -import { getGridModelsInfoOptions } from "@api"; +import { getDrilledWellboreHeadersOptions, getGridModelsInfoOptions } from "@api"; import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; @@ -8,6 +8,7 @@ import { GridLayerIRangeSetting } from "@modules/_shared/LayerFramework/settings import { GridLayerJRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting"; import { GridLayerKRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting"; import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; +import { IntersectionSetting } from "@modules/_shared/LayerFramework/settings/implementations/IntersectionSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; @@ -25,6 +26,7 @@ export class RealizationGridSettingsContext implements SettingsContext) { availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { const fieldIdentifier = getGlobalSetting("fieldId"); @@ -173,6 +176,30 @@ export class RealizationGridSettingsContext implements SettingsContext { const gridName = getLocalSetting(SettingType.GRID_NAME); const data = getHelperDependency(realizationGridDataDep); @@ -192,6 +219,33 @@ export class RealizationGridSettingsContext implements SettingsContext { + const wellboreHeaders = getHelperDependency(wellboreHeadersDep); + const intersectionPolylines = getGlobalSetting("intersectionPolylines"); + + if (!wellboreHeaders) { + return []; + } + + const intersectionOptions: { type: "wellbore" | "polyline"; name: string; uuid: string }[] = []; + for (const wellboreHeader of wellboreHeaders) { + intersectionOptions.push({ + type: "wellbore", + name: wellboreHeader.uniqueWellboreIdentifier, + uuid: wellboreHeader.wellboreUuid, + }); + } + for (const polyline of intersectionPolylines.getPolylines()) { + intersectionOptions.push({ + type: "polyline", + name: polyline.name, + uuid: polyline.id, + }); + } + + return intersectionOptions; + }); + availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { const gridName = getLocalSetting(SettingType.GRID_NAME); const gridAttribute = getLocalSetting(SettingType.ATTRIBUTE); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts index da1eb103d..ccd44881b 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts @@ -4,6 +4,7 @@ import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTy export type RealizationGridSettings = { [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; [SettingType.REALIZATION]: number | null; + [SettingType.INTERSECTION]: { type: "wellbore" | "polyline"; name: string; uuid: string } | null; [SettingType.ATTRIBUTE]: string | null; [SettingType.GRID_NAME]: string | null; [SettingType.GRID_LAYER_I_RANGE]: [number, number] | null; diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index 31ada2da3..1a64fb366 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -11,7 +11,7 @@ import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSub import { BoundingBox3D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; -import culori from "culori"; +import { converter } from "culori"; import { ContextMenu } from "./ContextMenu"; import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; @@ -45,11 +45,11 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const colorGenerator = React.useCallback( function* colorGenerator() { const colors: [number, number, number][] = colorSet.getColorArray().map((c) => { - const rgb = culori.converter("rgb")(c); + const rgb = converter("rgb")(c); if (!rgb) { return [0, 0, 0]; } - return [rgb.r, rgb.g, rgb.b]; + return [rgb.r * 255, rgb.g * 255, rgb.b * 255]; }); let i = 0; while (true) { diff --git a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts index c6a3dd04d..aa63df19a 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts @@ -15,6 +15,7 @@ import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/la import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; import { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataTransforms"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; +import { FenceMeshSection_trans, PolylineIntersection_trans } from "@modules/_shared/utils/wellbore"; import { TGrid3DColoringMode } from "@webviz/subsurface-viewer"; import { Grid3DLayer, MapLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; @@ -51,6 +52,14 @@ export function makeDeckGlLayer(layer: LayerInterface, colorScale?: Co return createWellPicksLayer(data, layer.getItemDelegate().getId()); } if (layer instanceof RealizationGridLayer) { + if (data.polylineIntersectionData) { + return makeIntersectionLayer( + data.polylineIntersectionData, + layer.getSettingsContext().getDelegate().getSettings().showGridLines.getDelegate().getValue(), + colorScale + ); + } + return makeGrid3DLayer( layer.getItemDelegate().getId(), data.gridSurfaceData, @@ -372,6 +381,102 @@ function makeGrid3DLayer( return grid3dLayer as unknown as WorkingGrid3dLayer; } +interface PolyDataVtk { + points: Float32Array; + polys: Uint32Array; + props: Float32Array; +} + +function buildVtkStylePolyDataFromFenceSections(fenceSections: FenceMeshSection_trans[]): PolyDataVtk { + // Calculate sizes of typed arrays + let totNumVertices = 0; + let totNumPolygons = 0; + let totNumConnectivities = 0; + for (const section of fenceSections) { + totNumVertices += section.verticesUzFloat32Arr.length / 2; + totNumPolygons += section.verticesPerPolyUintArr.length; + totNumConnectivities += section.polyIndicesUintArr.length; + } + + const pointsFloat32Arr = new Float32Array(3 * totNumVertices); + const polysUint32Arr = new Uint32Array(totNumPolygons + totNumConnectivities); + const polyPropsFloat32Arr = new Float32Array(totNumPolygons); + + let floatPointsDstIdx = 0; + let polysDstIdx = 0; + let propsDstIdx = 0; + for (const section of fenceSections) { + // uv to xyz + const directionX = section.end_utm_x - section.start_utm_x; + const directionY = section.end_utm_y - section.start_utm_y; + const magnitude = Math.sqrt(directionX ** 2 + directionY ** 2); + const unitDirectionX = directionX / magnitude; + const unitDirectionY = directionY / magnitude; + + const connOffset = floatPointsDstIdx / 3; + + for (let i = 0; i < section.verticesUzFloat32Arr.length; i += 2) { + const u = section.verticesUzFloat32Arr[i]; + const z = section.verticesUzFloat32Arr[i + 1]; + const x = u * unitDirectionX + section.start_utm_x; + const y = u * unitDirectionY + section.start_utm_y; + + pointsFloat32Arr[floatPointsDstIdx++] = x; + pointsFloat32Arr[floatPointsDstIdx++] = y; + pointsFloat32Arr[floatPointsDstIdx++] = z; + } + + // Fix poly indexes for each section + const numPolysInSection = section.verticesPerPolyUintArr.length; + let srcIdx = 0; + for (let i = 0; i < numPolysInSection; i++) { + const numVertsInPoly = section.verticesPerPolyUintArr[i]; + polysUint32Arr[polysDstIdx++] = numVertsInPoly; + + for (let j = 0; j < numVertsInPoly; j++) { + polysUint32Arr[polysDstIdx++] = section.polyIndicesUintArr[srcIdx++] + connOffset; + } + } + + polyPropsFloat32Arr.set(section.polyPropsFloat32Arr, propsDstIdx); + propsDstIdx += numPolysInSection; + } + + return { + points: pointsFloat32Arr, + polys: polysUint32Arr, + props: polyPropsFloat32Arr, + }; +} + +function makeIntersectionLayer( + polylineIntersectionData: PolylineIntersection_trans, + showGridLines: boolean, + colorScale: ColorScaleWithName +): WorkingGrid3dLayer { + const polyData = buildVtkStylePolyDataFromFenceSections(polylineIntersectionData.fenceMeshSections); + const grid3dIntersectionLayer = new Grid3DLayer({ + id: "grid-3d-intersection-layer", + pointsData: polyData.points as unknown as number[], + polysData: polyData.polys as unknown as number[], + propertiesData: polyData.props as unknown as number[], + colorMapName: "Continuous", + colorMapRange: [polylineIntersectionData.min_grid_prop_value, polylineIntersectionData.max_grid_prop_value], + colorMapClampColor: true, + coloringMode: TGrid3DColoringMode.Property, + colorMapFunction: makeColorMapFunction( + colorScale, + polylineIntersectionData.min_grid_prop_value, + polylineIntersectionData.max_grid_prop_value + ), + ZIncreasingDownwards: false, + gridLines: showGridLines, + material: { ambient: 0.4, diffuse: 0.7, shininess: 8, specularColor: [25, 25, 25] }, + pickable: false, + }); + return grid3dIntersectionLayer as unknown as WorkingGrid3dLayer; +} + function makeColorMapFunction( colorScale: ColorScaleWithName | undefined, valueMin: number, diff --git a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts index 293c0986a..a2b548b84 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts +++ b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts @@ -6,6 +6,7 @@ import { createEnsembleRealizationFilterFuncForWorkbenchSession, } from "@framework/WorkbenchSession"; import { WorkbenchSettings } from "@framework/WorkbenchSettings"; +import { IntersectionPolylines, IntersectionPolylinesEvent } from "@framework/userCreatedItems/IntersectionPolylines"; import { QueryClient } from "@tanstack/react-query"; import { isEqual } from "lodash"; @@ -38,6 +39,7 @@ export type GlobalSettings = { fieldId: string | null; ensembles: readonly RegularEnsemble[]; realizationFilterFunction: EnsembleRealizationFilterFunction; + intersectionPolylines: IntersectionPolylines; }; /* @@ -85,6 +87,13 @@ export class LayerManager implements Group, PublishSubscribe { + private _delegate: SettingDelegate; + + constructor() { + this._delegate = new SettingDelegate(null, this); + } + + getType(): SettingType { + return SettingType.INTERSECTION; + } + + getLabel(): string { + return "Intersection"; + } + + getDelegate(): SettingDelegate { + return this._delegate; + } + + isValueValid(availableValues: any[], value: ValueType | null): boolean { + if (value === null) { + return true; + } + + return availableValues.some((v) => v.uuid === value.uuid && v.type === value.type); + } + + makeComponent(): (props: SettingComponentProps) => React.ReactNode { + return function Realization(props: SettingComponentProps) { + const [type, setType] = React.useState<"wellbore" | "polyline" | "none">(props.value?.type ?? "none"); + function handleSelectionChange(selectedValue: string) { + const newValue = props.availableValues.find((v) => v.uuid === selectedValue) ?? null; + props.onValueChange(newValue); + } + + function handleCategoryChange(_: any, value: "wellbore" | "polyline" | "none") { + if (value === "none") { + props.onValueChange(null); + setType("none"); + } else { + setType(value); + const firstValue = props.availableValues.find((v) => v.type === value); + if (firstValue) { + props.onValueChange({ + ...firstValue, + }); + return; + } + + props.onValueChange(null); + } + } + + const options: DropdownOption[] = props.availableValues + .filter((value) => value.type === type) + .map((value) => { + return { + label: value.name, + value: value.uuid, + }; + }); + return ( +
+ + + options={options} + value={!props.isOverridden ? props.value?.uuid : props.overriddenValue?.uuid} + onChange={handleSelectionChange} + disabled={props.isOverridden || type === "none"} + showArrows + /> +
+ ); + }; + } +} + +SettingRegistry.registerSetting(IntersectionSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts index 19d9fd554..1efd74c0a 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts +++ b/frontend/src/modules/_shared/LayerFramework/settings/settingsTypes.ts @@ -4,6 +4,7 @@ export enum SettingType { STATISTIC_FUNCTION = "statisticFunction", SENSITIVITY = "sensitivity", SURFACE_NAME = "surfaceName", + INTERSECTION = "intersection", ATTRIBUTE = "attribute", TIME_OR_INTERVAL = "timeOrInterval", POLYGONS_ATTRIBUTE = "polygonsAttribute", From 342270d0484652ffdf80b0c95d34865ff6970080 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 3 Feb 2025 17:13:25 +0100 Subject: [PATCH 31/97] wip --- .../IntersectionRealizationGridLayer.ts | 223 ++++++++++++++++++ ...ersectionRealizationGridSettingsContext.ts | 215 +++++++++++++++++ .../IntersectionRealizationGridLayer/index.ts | 2 + .../IntersectionRealizationGridLayer/types.ts | 13 + .../RealizationGridLayer.ts | 193 +-------------- .../RealizationGridSettingsContext.ts | 56 +---- .../RealizationGridLayer/types.ts | 1 - .../layerManagerComponentWrapper.tsx | 14 ++ .../view/components/ReadoutWrapper.tsx | 6 +- .../3DViewerNew/view/components/Toolbar.tsx | 80 ++++--- .../view/utils/PolylinesPlugin.tsx | 4 + .../3DViewerNew/view/utils/layerFactory.ts | 16 +- .../delegates/_utils/Dependency.ts | 4 + .../framework/LayerManager/LayerManager.ts | 13 +- .../implementations/IntersectionSetting.tsx | 63 ++--- 15 files changed, 589 insertions(+), 314 deletions(-) create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/index.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/types.ts diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts new file mode 100644 index 000000000..b4251b9b5 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts @@ -0,0 +1,223 @@ +import { getWellTrajectoriesOptions, postGetPolylineIntersectionOptions } from "@api"; +import { IntersectionReferenceSystem } from "@equinor/esv-intersection"; +import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; +import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; +import { + PolylineIntersection_trans, + calcExtendedSimplifiedWellboreTrajectoryInXYPlane, + transformPolylineIntersection, +} from "@modules/_shared/utils/wellbore"; +import { QueryClient } from "@tanstack/react-query"; + +import { isEqual } from "lodash"; + +import { IntersectionRealizationGridSettingsContext } from "./IntersectionRealizationGridSettingsContext"; +import { IntersectionRealizationGridSettings } from "./types"; + +export class IntersectionRealizationGridLayer + implements + Layer< + IntersectionRealizationGridSettings, + { + polylineIntersectionData: PolylineIntersection_trans | null; + } + > +{ + private _layerDelegate: LayerDelegate< + IntersectionRealizationGridSettings, + { + polylineIntersectionData: PolylineIntersection_trans | null; + } + >; + private _itemDelegate: ItemDelegate; + + constructor(layerManager: LayerManager) { + this._itemDelegate = new ItemDelegate("Intersection Realization Grid", layerManager); + this._layerDelegate = new LayerDelegate( + this, + layerManager, + new IntersectionRealizationGridSettingsContext(layerManager), + LayerColoringType.COLORSCALE + ); + } + + getSettingsContext() { + return this._layerDelegate.getSettingsContext(); + } + + getItemDelegate(): ItemDelegate { + return this._itemDelegate; + } + + getLayerDelegate(): LayerDelegate< + IntersectionRealizationGridSettings, + { + polylineIntersectionData: PolylineIntersection_trans | null; + } + > { + return this._layerDelegate; + } + + doSettingsChangesRequireDataRefetch( + prevSettings: IntersectionRealizationGridSettings, + newSettings: IntersectionRealizationGridSettings + ): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeBoundingBox(): BoundingBox | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + if (data.polylineIntersectionData) { + return null; + } + + return null; + } + + makeValueRange(): [number, number] | null { + const data = this._layerDelegate.getData(); + if (!data) { + return null; + } + + if (data.polylineIntersectionData) { + return [ + data.polylineIntersectionData.min_grid_prop_value, + data.polylineIntersectionData.max_grid_prop_value, + ]; + } + + return null; + } + + fetchData(queryClient: QueryClient): Promise<{ + polylineIntersectionData: PolylineIntersection_trans | null; + }> { + const settings = this.getSettingsContext().getDelegate().getSettings(); + const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); + const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); + const intersection = settings[SettingType.INTERSECTION].getDelegate().getValue(); + const gridName = settings[SettingType.GRID_NAME].getDelegate().getValue(); + const parameterName = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); + let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + if (timeOrInterval === "NO_TIME") { + timeOrInterval = null; + } + + const fieldIdentifier = this._itemDelegate.getLayerManager().getGlobalSetting("fieldId"); + + const queryKey = [ + "gridIntersection", + ensembleIdent, + gridName, + parameterName, + timeOrInterval, + realizationNum, + intersection, + ]; + this._layerDelegate.registerQueryKey(queryKey); + + if (!intersection) { + return Promise.resolve({ + polylineIntersectionData: null, + }); + } + + let makePolylinePromise: Promise = new Promise((resolve) => { + if (intersection.type === "wellbore") { + return queryClient + .fetchQuery({ + ...getWellTrajectoriesOptions({ + query: { + field_identifier: fieldIdentifier ?? "", + wellbore_uuids: [intersection.uuid], + }, + }), + }) + .then((data) => { + const path: number[][] = []; + for (const [index, northing] of data[0].northingArr.entries()) { + const easting = data[0].eastingArr[index]; + const tvd_msl = data[0].tvdMslArr[index]; + + path.push([easting, northing, tvd_msl]); + } + const offset = data[0].tvdMslArr[0]; + + const intersectionReferenceSystem = new IntersectionReferenceSystem(path); + intersectionReferenceSystem.offset = offset; + + const polylineUtmXy: number[] = []; + polylineUtmXy.push( + ...calcExtendedSimplifiedWellboreTrajectoryInXYPlane( + path, + 0, + 5 + ).simplifiedWellboreTrajectoryXy.flat() + ); + + resolve(polylineUtmXy); + }); + } else { + const intersectionPolyline = this._itemDelegate + .getLayerManager() + .getWorkbenchSession() + .getUserCreatedItems() + .getIntersectionPolylines() + .getPolyline(intersection.uuid); + if (!intersectionPolyline) { + resolve([]); + return; + } + + const polylineUtmXy: number[] = []; + for (const point of intersectionPolyline.path) { + polylineUtmXy.push(point[0], point[1]); + } + + resolve(polylineUtmXy); + } + }); + + const gridIntersectionPromise = makePolylinePromise + .then((polyline_utm_xy) => + queryClient.fetchQuery({ + ...postGetPolylineIntersectionOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + grid_name: gridName ?? "", + parameter_name: parameterName ?? "", + parameter_time_or_interval_str: timeOrInterval, + realization_num: realizationNum ?? 0, + }, + body: { polyline_utm_xy }, + }), + }) + ) + .then(transformPolylineIntersection) + .then((data) => ({ + polylineIntersectionData: data, + })); + + return gridIntersectionPromise; + } + + serializeState(): SerializedLayer { + return this._layerDelegate.serializeState(); + } + + deserializeState(serializedState: SerializedLayer): void { + this._layerDelegate.deserializeState(serializedState); + } +} + +LayerRegistry.registerLayer(IntersectionRealizationGridLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts new file mode 100644 index 000000000..fc43394a5 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts @@ -0,0 +1,215 @@ +import { getDrilledWellboreHeadersOptions, getGridModelsInfoOptions } from "@api"; +import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; +import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; +import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; +import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; +import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; +import { + IntersectionSetting, + IntersectionSettingValue, +} from "@modules/_shared/LayerFramework/settings/implementations/IntersectionSetting"; +import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; +import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; +import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +import { IntersectionRealizationGridSettings } from "./types"; + +export class IntersectionRealizationGridSettingsContext + implements SettingsContext +{ + private _contextDelegate: SettingsContextDelegate; + + constructor(layerManager: LayerManager) { + this._contextDelegate = new SettingsContextDelegate< + IntersectionRealizationGridSettings, + keyof IntersectionRealizationGridSettings + >(this, layerManager, { + [SettingType.INTERSECTION]: new IntersectionSetting(), + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.REALIZATION]: new RealizationSetting(), + [SettingType.GRID_NAME]: new GridNameSetting(), + [SettingType.ATTRIBUTE]: new AttributeSetting(), + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + [SettingType.SHOW_GRID_LINES]: new ShowGridLinesSetting(), + }); + } + + areCurrentSettingsValid(settings: IntersectionRealizationGridSettings): boolean { + return ( + settings[SettingType.INTERSECTION] !== null && + settings[SettingType.ENSEMBLE] !== null && + settings[SettingType.REALIZATION] !== null && + settings[SettingType.GRID_NAME] !== null && + settings[SettingType.ATTRIBUTE] !== null && + settings[SettingType.TIME_OR_INTERVAL] !== null + ); + } + + getDelegate(): SettingsContextDelegate { + return this._contextDelegate; + } + + getSettings() { + return this._contextDelegate.getSettings(); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + queryClient, + workbenchSession, + }: DefineDependenciesArgs) { + availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); + + if (!ensembleIdent) { + return []; + } + + const realizations = realizationFilterFunc(ensembleIdent); + + return [...realizations]; + }); + + const realizationGridDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + const realization = getLocalSetting(SettingType.REALIZATION); + + if (!ensembleIdent || realization === null) { + return null; + } + + return await queryClient.fetchQuery({ + ...getGridModelsInfoOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + realization_num: realization, + }, + signal: abortSignal, + }), + }); + }); + + availableSettingsUpdater(SettingType.GRID_NAME, ({ getHelperDependency }) => { + const data = getHelperDependency(realizationGridDataDep); + + if (!data) { + return []; + } + + const availableGridNames = [...Array.from(new Set(data.map((gridModelInfo) => gridModelInfo.grid_name)))]; + + return availableGridNames; + }); + + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getLocalSetting, getHelperDependency }) => { + const gridName = getLocalSetting(SettingType.GRID_NAME); + const data = getHelperDependency(realizationGridDataDep); + + if (!gridName || !data) { + return []; + } + + const gridAttributeArr = + data.find((gridModel) => gridModel.grid_name === gridName)?.property_info_arr ?? []; + + const availableGridAttributes = [ + ...Array.from(new Set(gridAttributeArr.map((gridAttribute) => gridAttribute.property_name))), + ]; + + return availableGridAttributes; + }); + + const wellboreHeadersDep = helperDependency(async function fetchData({ getLocalSetting, abortSignal }) { + const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); + + if (!ensembleIdent) { + return null; + } + + const ensembleSet = workbenchSession.getEnsembleSet(); + const ensemble = ensembleSet.findEnsemble(ensembleIdent); + + if (!ensemble) { + return null; + } + + const fieldIdentifier = ensemble.getFieldIdentifier(); + + return await queryClient.fetchQuery({ + ...getDrilledWellboreHeadersOptions({ + query: { field_identifier: fieldIdentifier }, + signal: abortSignal, + }), + }); + }); + + availableSettingsUpdater(SettingType.INTERSECTION, ({ getHelperDependency, getGlobalSetting }) => { + const wellboreHeaders = getHelperDependency(wellboreHeadersDep); + const intersectionPolylines = getGlobalSetting("intersectionPolylines"); + + if (!wellboreHeaders) { + return []; + } + + const intersectionOptions: IntersectionSettingValue[] = []; + for (const wellboreHeader of wellboreHeaders) { + intersectionOptions.push({ + type: "wellbore", + name: wellboreHeader.uniqueWellboreIdentifier, + uuid: wellboreHeader.wellboreUuid, + }); + } + + for (const polyline of intersectionPolylines) { + intersectionOptions.push({ + type: "polyline", + name: polyline.name, + uuid: polyline.id, + }); + } + + return intersectionOptions; + }); + + availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const gridName = getLocalSetting(SettingType.GRID_NAME); + const gridAttribute = getLocalSetting(SettingType.ATTRIBUTE); + const data = getHelperDependency(realizationGridDataDep); + + if (!gridName || !gridAttribute || !data) { + return []; + } + + const gridAttributeArr = + data.find((gridModel) => gridModel.grid_name === gridName)?.property_info_arr ?? []; + + const availableTimeOrIntervals = [ + ...Array.from( + new Set( + gridAttributeArr + .filter((attr) => attr.property_name === gridAttribute) + .map((gridAttribute) => gridAttribute.iso_date_or_interval ?? "NO_TIME") + ) + ), + ]; + + return availableTimeOrIntervals; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/index.ts new file mode 100644 index 000000000..037b38884 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/index.ts @@ -0,0 +1,2 @@ +export { IntersectionRealizationGridLayer } from "./IntersectionRealizationGridLayer"; +export { IntersectionRealizationGridSettingsContext } from "./IntersectionRealizationGridSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/types.ts new file mode 100644 index 000000000..3848bedff --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/types.ts @@ -0,0 +1,13 @@ +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { IntersectionSettingValue } from "@modules/_shared/LayerFramework/settings/implementations/IntersectionSetting"; +import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; + +export type IntersectionRealizationGridSettings = { + [SettingType.INTERSECTION]: IntersectionSettingValue | null; + [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; + [SettingType.REALIZATION]: number | null; + [SettingType.ATTRIBUTE]: string | null; + [SettingType.GRID_NAME]: string | null; + [SettingType.TIME_OR_INTERVAL]: string | null; + [SettingType.SHOW_GRID_LINES]: boolean; +}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts index 55df970ed..ce1d84be9 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -1,11 +1,4 @@ -import { - getGridParameterOptions, - getGridSurfaceOptions, - getWellTrajectoriesOptions, - postGetPolylineIntersectionOptions, -} from "@api"; -import { IntersectionReferenceSystem } from "@equinor/esv-intersection"; -import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { getGridParameterOptions, getGridSurfaceOptions } from "@api"; import { GridMappedProperty_trans, GridSurface_trans, @@ -18,11 +11,6 @@ import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerMan import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { - PolylineIntersection_trans, - calcExtendedSimplifiedWellboreTrajectoryInXYPlane, - transformPolylineIntersection, -} from "@modules/_shared/utils/wellbore"; import { QueryClient } from "@tanstack/react-query"; import { isEqual } from "lodash"; @@ -37,7 +25,6 @@ export class RealizationGridLayer { gridSurfaceData: GridSurface_trans | null; gridParameterData: GridMappedProperty_trans | null; - polylineIntersectionData: PolylineIntersection_trans | null; } > { @@ -46,7 +33,6 @@ export class RealizationGridLayer { gridSurfaceData: GridSurface_trans | null; gridParameterData: GridMappedProperty_trans | null; - polylineIntersectionData: PolylineIntersection_trans | null; } >; private _itemDelegate: ItemDelegate; @@ -74,7 +60,6 @@ export class RealizationGridLayer { gridSurfaceData: GridSurface_trans | null; gridParameterData: GridMappedProperty_trans | null; - polylineIntersectionData: PolylineIntersection_trans | null; } > { return this._layerDelegate; @@ -93,10 +78,6 @@ export class RealizationGridLayer return null; } - if (data.polylineIntersectionData) { - return null; - } - if (data.gridSurfaceData) { return { x: [ @@ -120,13 +101,6 @@ export class RealizationGridLayer return null; } - if (data.polylineIntersectionData) { - return [ - data.polylineIntersectionData.min_grid_prop_value, - data.polylineIntersectionData.max_grid_prop_value, - ]; - } - if (data.gridParameterData) { return [data.gridParameterData.min_grid_prop_value, data.gridParameterData.max_grid_prop_value]; } @@ -137,17 +111,15 @@ export class RealizationGridLayer fetchData(queryClient: QueryClient): Promise<{ gridSurfaceData: GridSurface_trans | null; gridParameterData: GridMappedProperty_trans | null; - polylineIntersectionData: PolylineIntersection_trans | null; }> { const settings = this.getSettingsContext().getDelegate().getSettings(); const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); - const intersection = settings[SettingType.INTERSECTION].getDelegate().getValue(); const gridName = settings[SettingType.GRID_NAME].getDelegate().getValue(); - const attribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - if (timeOrInterval === "NO_TIME") { - timeOrInterval = null; + const parameterName = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); + let parameterTimeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + if (parameterTimeOrInterval === "NO_TIME") { + parameterTimeOrInterval = null; } let availableDimensions = settings[SettingType.GRID_LAYER_K_RANGE].getDelegate().getAvailableValues(); if (!availableDimensions.length || availableDimensions[0] === null) { @@ -165,160 +137,6 @@ export class RealizationGridLayer const kMin = layerKRange?.[0] ?? 0; const kMax = layerKRange?.[1] ?? 0; - if (!intersection) { - return this.fetchGridParameterAndSurface( - queryClient, - ensembleIdent, - gridName, - attribute, - timeOrInterval, - realizationNum, - iMin, - iMax, - jMin, - jMax, - kMin, - kMax - ); - } - - return this.fetchPolylineIntersection( - queryClient, - ensembleIdent, - gridName, - attribute, - timeOrInterval, - realizationNum, - intersection - ); - } - - private fetchPolylineIntersection( - queryClient: QueryClient, - ensembleIdent: RegularEnsembleIdent | null, - gridName: string | null, - parameterName: string | null, - timeOrInterval: string | null, - realizationNum: number | null, - intersection: { type: "wellbore" | "polyline"; name: string; uuid: string } - ): Promise<{ - gridSurfaceData: GridSurface_trans | null; - gridParameterData: GridMappedProperty_trans | null; - polylineIntersectionData: PolylineIntersection_trans | null; - }> { - const fieldIdentifier = this._itemDelegate.getLayerManager().getGlobalSetting("fieldId"); - - const queryKey = [ - "gridIntersection", - ensembleIdent, - gridName, - parameterName, - timeOrInterval, - realizationNum, - intersection, - ]; - this._layerDelegate.registerQueryKey(queryKey); - - let makePolylinePromise: Promise = new Promise((resolve) => { - if (intersection.type === "wellbore") { - return queryClient - .fetchQuery({ - ...getWellTrajectoriesOptions({ - query: { - field_identifier: fieldIdentifier ?? "", - wellbore_uuids: [intersection.uuid], - }, - }), - }) - .then((data) => { - const path: number[][] = []; - for (const [index, northing] of data[0].northingArr.entries()) { - const easting = data[0].eastingArr[index]; - const tvd_msl = data[0].tvdMslArr[index]; - - path.push([easting, northing, tvd_msl]); - } - const offset = data[0].tvdMslArr[0]; - - const intersectionReferenceSystem = new IntersectionReferenceSystem(path); - intersectionReferenceSystem.offset = offset; - - const polylineUtmXy: number[] = []; - polylineUtmXy.push( - ...calcExtendedSimplifiedWellboreTrajectoryInXYPlane( - path, - 0, - 5 - ).simplifiedWellboreTrajectoryXy.flat() - ); - - resolve(polylineUtmXy); - }); - } else { - const intersectionPolyline = this._itemDelegate - .getLayerManager() - .getWorkbenchSession() - .getUserCreatedItems() - .getIntersectionPolylines() - .getPolyline(intersection.uuid); - if (!intersectionPolyline) { - resolve([]); - return; - } - - const polylineUtmXy: number[] = []; - for (const point of intersectionPolyline.path) { - polylineUtmXy.push(point[0], point[1]); - } - - resolve(polylineUtmXy); - } - }); - - const gridIntersectionPromise = makePolylinePromise - .then((polyline_utm_xy) => - queryClient.fetchQuery({ - ...postGetPolylineIntersectionOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - grid_name: gridName ?? "", - parameter_name: parameterName ?? "", - parameter_time_or_interval_str: timeOrInterval, - realization_num: realizationNum ?? 0, - }, - body: { polyline_utm_xy }, - }), - }) - ) - .then(transformPolylineIntersection) - .then((data) => ({ - polylineIntersectionData: data, - gridSurfaceData: null, - gridParameterData: null, - })); - - return gridIntersectionPromise; - } - - private fetchGridParameterAndSurface( - queryClient: QueryClient, - ensembleIdent: RegularEnsembleIdent | null, - gridName: string | null, - parameterName: string | null, - parameterTimeOrInterval: string | null, - realizationNum: number | null, - iMin: number, - iMax: number, - jMin: number, - jMax: number, - kMin: number, - kMax: number - ): Promise<{ - gridSurfaceData: GridSurface_trans | null; - gridParameterData: GridMappedProperty_trans | null; - polylineIntersectionData: PolylineIntersection_trans | null; - }> { const queryKey = [ "gridParameter", ensembleIdent, @@ -378,7 +196,6 @@ export class RealizationGridLayer return Promise.all([gridSurfacePromise, gridParameterPromise]).then(([gridSurfaceData, gridParameterData]) => ({ gridSurfaceData, gridParameterData, - polylineIntersectionData: null, })); } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts index 8f34f5442..81dd065fc 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts @@ -1,4 +1,4 @@ -import { getDrilledWellboreHeadersOptions, getGridModelsInfoOptions } from "@api"; +import { getGridModelsInfoOptions } from "@api"; import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; @@ -8,7 +8,6 @@ import { GridLayerIRangeSetting } from "@modules/_shared/LayerFramework/settings import { GridLayerJRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting"; import { GridLayerKRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting"; import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; -import { IntersectionSetting } from "@modules/_shared/LayerFramework/settings/implementations/IntersectionSetting"; import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; @@ -26,7 +25,6 @@ export class RealizationGridSettingsContext implements SettingsContext { const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); const realization = getLocalSetting(SettingType.REALIZATION); @@ -176,30 +175,6 @@ export class RealizationGridSettingsContext implements SettingsContext { const gridName = getLocalSetting(SettingType.GRID_NAME); const data = getHelperDependency(realizationGridDataDep); @@ -219,33 +194,6 @@ export class RealizationGridSettingsContext implements SettingsContext { - const wellboreHeaders = getHelperDependency(wellboreHeadersDep); - const intersectionPolylines = getGlobalSetting("intersectionPolylines"); - - if (!wellboreHeaders) { - return []; - } - - const intersectionOptions: { type: "wellbore" | "polyline"; name: string; uuid: string }[] = []; - for (const wellboreHeader of wellboreHeaders) { - intersectionOptions.push({ - type: "wellbore", - name: wellboreHeader.uniqueWellboreIdentifier, - uuid: wellboreHeader.wellboreUuid, - }); - } - for (const polyline of intersectionPolylines.getPolylines()) { - intersectionOptions.push({ - type: "polyline", - name: polyline.name, - uuid: polyline.id, - }); - } - - return intersectionOptions; - }); - availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { const gridName = getLocalSetting(SettingType.GRID_NAME); const gridAttribute = getLocalSetting(SettingType.ATTRIBUTE); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts index ccd44881b..da1eb103d 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts @@ -4,7 +4,6 @@ import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTy export type RealizationGridSettings = { [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; [SettingType.REALIZATION]: number | null; - [SettingType.INTERSECTION]: { type: "wellbore" | "polyline"; name: string; uuid: string } | null; [SettingType.ATTRIBUTE]: string | null; [SettingType.GRID_NAME]: string | null; [SettingType.GRID_LAYER_I_RANGE]: [number, number] | null; diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index 71de7cd51..9de378db8 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -11,6 +11,7 @@ import { MenuItem } from "@lib/components/MenuItem"; import { ObservedSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer"; import { StatisticalSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; import { PreferredViewLayout } from "@modules/2DViewer/types"; +import { IntersectionRealizationGridLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer"; import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer"; import { EnsembleSetting } from "@modules//_shared/LayerFramework/settings/implementations/EnsembleSetting"; @@ -82,6 +83,9 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper case "color-scale": groupDelegate.prependChild(new ColorScale("Color scale", props.layerManager)); return; + case "intersection-realization-grid": + groupDelegate.insertChild(new IntersectionRealizationGridLayer(props.layerManager), numSharedSettings); + return; case "realization-grid": groupDelegate.insertChild(new RealizationGridLayer(props.layerManager), numSharedSettings); return; @@ -248,6 +252,16 @@ const LAYER_ACTIONS: LayersActionGroup[] = [ }, ], }, + { + label: "Intersection", + children: [ + { + identifier: "intersection-realization-grid", + icon: , + label: "Intersection Realization Grid", + }, + ], + }, { label: "Reservoir grid", children: [ diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index 1a64fb366..ac2b70f35 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -72,8 +72,10 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const unsubscribeFromPolylinesPlugin = polylinesPlugin .getPublishSubscribeDelegate() - .makeSubscriberFunction(PolylinesPluginTopic.POLYLINES)(() => { - intersectionPolylines.setPolylines(polylinesPlugin.getPolylines()); + .makeSubscriberFunction(PolylinesPluginTopic.EDITING_POLYLINE_ID)(() => { + if (polylinesPlugin.getCurrentEditingPolylineId() === null) { + intersectionPolylines.setPolylines(polylinesPlugin.getPolylines()); + } }); const unsubscribeFromIntersectionPolylines = intersectionPolylines.subscribe( diff --git a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx index bdbfff064..1b0278b7b 100644 --- a/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/Toolbar.tsx @@ -5,9 +5,19 @@ import { HoldPressedIntervalCallbackButton } from "@lib/components/HoldPressedIn import { Input } from "@lib/components/Input"; import { ToggleButton } from "@lib/components/ToggleButton"; import { AddPathPointIcon, DrawPathIcon, RemovePathPointIcon } from "@lib/icons/"; +import { resolveClassNames } from "@lib/utils/resolveClassNames"; import { Toolbar as GenericToolbar, ToolBarDivider } from "@modules/_shared/components/Toolbar"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; -import { Add, FilterCenterFocus, GridOff, GridOn, Polyline, Remove } from "@mui/icons-material"; +import { + Add, + FilterCenterFocus, + GridOff, + GridOn, + KeyboardDoubleArrowLeft, + KeyboardDoubleArrowRight, + Polyline, + Remove, +} from "@mui/icons-material"; import { PolylineEditingMode } from "../hooks/editablePolylines/types"; import { PolylinesPlugin, PolylinesPluginTopic } from "../utils/PolylinesPlugin"; @@ -24,6 +34,7 @@ export type ToolbarProps = { }; export function Toolbar(props: ToolbarProps): React.ReactNode { + const [expanded, setExpanded] = React.useState(false); const [gridVisible, setGridVisible] = React.useState(false); const [polylineName, setPolylineName] = React.useState(null); const [prevEditingPolylineId, setPrevEditingPolylineId] = React.useState(null); @@ -76,40 +87,53 @@ export function Toolbar(props: ToolbarProps): React.ReactNode { return ( -
+
- - {gridVisible ? : } - +
+ + {gridVisible ? : } + + + + + + + + + + + {props.verticalScale.toFixed(2)} + + + + +
- setExpanded(!expanded)} > - - - - - - - - {props.verticalScale.toFixed(2)} - - - - + {expanded ? ( + + ) : ( + + )} +
- {polylineEditingMode !== PolylineEditingMode.NONE && ( + {polylineEditingMode !== PolylineEditingMode.NONE && expanded && ( <>
, colorScale?: Co if (layer instanceof DrilledWellborePicksLayer) { return createWellPicksLayer(data, layer.getItemDelegate().getId()); } + if (layer instanceof IntersectionRealizationGridLayer) { + return makeIntersectionLayer( + data.polylineIntersectionData, + layer.getSettingsContext().getDelegate().getSettings().showGridLines.getDelegate().getValue(), + colorScale + ); + } if (layer instanceof RealizationGridLayer) { - if (data.polylineIntersectionData) { - return makeIntersectionLayer( - data.polylineIntersectionData, - layer.getSettingsContext().getDelegate().getSettings().showGridLines.getDelegate().getValue(), - colorScale - ); - } - return makeGrid3DLayer( layer.getItemDelegate().getId(), data.gridSurfaceData, diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/_utils/Dependency.ts b/frontend/src/modules/_shared/LayerFramework/delegates/_utils/Dependency.ts index 911d67df5..00654a7eb 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/_utils/Dependency.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/_utils/Dependency.ts @@ -121,6 +121,10 @@ export class Dependency { + const cachedValue = this._cachedGlobalSettingsMap.get(settingName as string); + if (isEqual(value, cachedValue)) { + return; + } this._cachedGlobalSettingsMap.set(settingName as string, value); this.callUpdateFunc(); }); diff --git a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts index a2b548b84..527263d73 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts +++ b/frontend/src/modules/_shared/LayerFramework/framework/LayerManager/LayerManager.ts @@ -6,7 +6,7 @@ import { createEnsembleRealizationFilterFuncForWorkbenchSession, } from "@framework/WorkbenchSession"; import { WorkbenchSettings } from "@framework/WorkbenchSettings"; -import { IntersectionPolylines, IntersectionPolylinesEvent } from "@framework/userCreatedItems/IntersectionPolylines"; +import { IntersectionPolyline, IntersectionPolylinesEvent } from "@framework/userCreatedItems/IntersectionPolylines"; import { QueryClient } from "@tanstack/react-query"; import { isEqual } from "lodash"; @@ -39,7 +39,7 @@ export type GlobalSettings = { fieldId: string | null; ensembles: readonly RegularEnsemble[]; realizationFilterFunction: EnsembleRealizationFilterFunction; - intersectionPolylines: IntersectionPolylines; + intersectionPolylines: IntersectionPolyline[]; }; /* @@ -208,7 +208,10 @@ export class LayerManager implements Group, PublishSubscribe { - private _delegate: SettingDelegate; +export class IntersectionSetting implements Setting { + private _delegate: SettingDelegate; constructor() { - this._delegate = new SettingDelegate(null, this); + this._delegate = new SettingDelegate(null, this); } getType(): SettingType { @@ -29,42 +29,49 @@ export class IntersectionSetting implements Setting { return "Intersection"; } - getDelegate(): SettingDelegate { + getDelegate(): SettingDelegate { return this._delegate; } - isValueValid(availableValues: any[], value: ValueType | null): boolean { + isValueValid(availableValues: any[], value: IntersectionSettingValue | null): boolean { if (value === null) { - return true; + return false; } return availableValues.some((v) => v.uuid === value.uuid && v.type === value.type); } - makeComponent(): (props: SettingComponentProps) => React.ReactNode { - return function Realization(props: SettingComponentProps) { - const [type, setType] = React.useState<"wellbore" | "polyline" | "none">(props.value?.type ?? "none"); + fixupValue(availableValues: any[], currentValue: IntersectionSettingValue | null): IntersectionSettingValue | null { + if (currentValue === null) { + return availableValues.find((v) => v.type === "wellbore") ?? null; + } + + if (availableValues.some((v) => v.uuid === currentValue.uuid && v.type === currentValue.type)) { + return currentValue; + } + + return availableValues.find((v) => v.type === currentValue.type) ?? null; + } + + makeComponent(): (props: SettingComponentProps) => React.ReactNode { + return function Realization(props: SettingComponentProps) { + const [type, setType] = React.useState(props.value?.type ?? "wellbore"); function handleSelectionChange(selectedValue: string) { const newValue = props.availableValues.find((v) => v.uuid === selectedValue) ?? null; props.onValueChange(newValue); } - function handleCategoryChange(_: any, value: "wellbore" | "polyline" | "none") { - if (value === "none") { - props.onValueChange(null); - setType("none"); - } else { - setType(value); - const firstValue = props.availableValues.find((v) => v.type === value); - if (firstValue) { - props.onValueChange({ - ...firstValue, - }); - return; - } - - props.onValueChange(null); + function handleCategoryChange(_: any, value: IntersectionSettingValue["type"]) { + setType(value); + const firstValue = props.availableValues.find((v) => v.type === value); + if (firstValue) { + props.onValueChange({ + ...firstValue, + }); + return; } + + props.onValueChange(null); } const options: DropdownOption[] = props.availableValues @@ -80,10 +87,6 @@ export class IntersectionSetting implements Setting { { options={options} value={!props.isOverridden ? props.value?.uuid : props.overriddenValue?.uuid} onChange={handleSelectionChange} - disabled={props.isOverridden || type === "none"} + disabled={props.isOverridden} showArrows />
From fa0cc01e3386cc8bbcec67d22c4dbc03763d2810 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 4 Feb 2025 17:19:27 +0100 Subject: [PATCH 32/97] wip --- .../2DViewer/view/utils/layerFactory.ts | 2 +- .../customDeckGlLayers/WellborePicksLayer.ts | 121 --------- .../3DViewerNew/view/utils/layerFactory.ts | 2 +- .../layers/LayerVisualizationFactory.ts | 250 ++++++++++++++++++ .../customDeckGlLayers/WellborePicksLayer.ts | 0 5 files changed, 252 insertions(+), 123 deletions(-) delete mode 100644 frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts create mode 100644 frontend/src/modules/_shared/LayerFramework/layers/LayerVisualizationFactory.ts rename frontend/src/modules/{2DViewer/view => _shared}/customDeckGlLayers/WellborePicksLayer.ts (100%) diff --git a/frontend/src/modules/2DViewer/view/utils/layerFactory.ts b/frontend/src/modules/2DViewer/view/utils/layerFactory.ts index 7e7f2329c..a39232530 100644 --- a/frontend/src/modules/2DViewer/view/utils/layerFactory.ts +++ b/frontend/src/modules/2DViewer/view/utils/layerFactory.ts @@ -9,6 +9,7 @@ import { Layer as LayerInterface } from "@modules/_shared/LayerFramework/interfa import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; +import { WellBorePickLayerData, WellborePicksLayer } from "@modules_shared/customDeckGlLayers/WellborePicksLayer"; import { ColormapLayer, Grid3DLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; import { Rgb, parse } from "culori"; @@ -20,7 +21,6 @@ import { RealizationPolygonsLayer } from "../../LayerFramework/customLayerImplem import { RealizationSurfaceLayer } from "../../LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { StatisticalSurfaceLayer } from "../../LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; import { AdvancedWellsLayer } from "../customDeckGlLayers/AdvancedWellsLayer"; -import { WellBorePickLayerData, WellborePicksLayer } from "../customDeckGlLayers/WellborePicksLayer"; export function makeDeckGlLayer(layer: LayerInterface, colorScale?: ColorScaleWithName): Layer | null { const data = layer.getLayerDelegate().getData(); diff --git a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts b/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts deleted file mode 100644 index fe97dfa76..000000000 --- a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/WellborePicksLayer.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { CompositeLayer, CompositeLayerProps, FilterContext, Layer, UpdateParameters } from "@deck.gl/core"; -import { GeoJsonLayer, TextLayer } from "@deck.gl/layers"; - -import type { Feature, FeatureCollection } from "geojson"; - -export type WellBorePickLayerData = { - easting: number; - northing: number; - wellBoreUwi: string; - tvdMsl: number; - md: number; - slotName: string; -}; - -type TextLayerData = { - coordinates: [number, number, number]; - name: string; -}; - -export type WellBorePicksLayerProps = { - id: string; - data: WellBorePickLayerData[]; -}; - -export class WellborePicksLayer extends CompositeLayer { - static layerName: string = "WellborePicksLayer"; - private _textData: TextLayerData[] = []; - private _pointsData: FeatureCollection | null = null; - - filterSubLayer(context: FilterContext): boolean { - if (context.layer.id.includes("text")) { - return context.viewport.zoom > -4; - } - - return true; - } - - updateState(params: UpdateParameters>>): void { - const features: Feature[] = params.props.data.map((wellPick) => { - return { - type: "Feature", - geometry: { - type: "Point", - coordinates: [wellPick.easting, wellPick.northing, -wellPick.tvdMsl], - }, - properties: { - name: `${wellPick.wellBoreUwi}, TVD_MSL: ${wellPick.tvdMsl}, MD: ${wellPick.md}`, - color: [100, 100, 100, 100], - }, - }; - }); - - const pointsData: FeatureCollection = { - type: "FeatureCollection", - features: features, - }; - - const textData: TextLayerData[] = this.props.data.map((wellPick) => { - return { - coordinates: [wellPick.easting, wellPick.northing, -wellPick.tvdMsl], - name: wellPick.wellBoreUwi, - }; - }); - - this._pointsData = pointsData; - this._textData = textData; - } - - renderLayers() { - const fontSize = 16; - const sizeMinPixels = 16; - const sizeMaxPixels = 16; - - return [ - new GeoJsonLayer( - this.getSubLayerProps({ - id: "points", - data: this._pointsData ?? undefined, - filled: true, - lineWidthMinPixels: 5, - lineWidthMaxPixels: 5, - lineWidthUnits: "meters", - parameters: { - depthTest: false, - }, - getLineWidth: 1, - depthTest: false, - pickable: true, - getText: (d: Feature) => d.properties?.wellBoreUwi, - getLineColor: [50, 50, 50], - }) - ), - - new TextLayer( - this.getSubLayerProps({ - id: "text", - data: this._textData, - pickable: true, - getColor: [255, 255, 255], - fontWeight: 800, - fontSettings: { - fontSize: fontSize * 2, - sdf: true, - }, - outlineColor: [0, 0, 0], - outlineWidth: 2, - getSize: 12, - sdf: true, - sizeScale: fontSize, - sizeUnits: "meters", - sizeMinPixels: sizeMinPixels, - sizeMaxPixels: sizeMaxPixels, - getAlignmentBaseline: "top", - getTextAnchor: "middle", - getPosition: (d: TextLayerData) => d.coordinates, - getText: (d: TextLayerData) => d.name, - }) - ), - ]; - } -} diff --git a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts index 06559c46d..63f99ebe8 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts +++ b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts @@ -17,6 +17,7 @@ import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layer import { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataTransforms"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; import { FenceMeshSection_trans, PolylineIntersection_trans } from "@modules/_shared/utils/wellbore"; +import { WellBorePickLayerData, WellborePicksLayer } from "@modules_shared/customDeckGlLayers/WellborePicksLayer"; import { TGrid3DColoringMode } from "@webviz/subsurface-viewer"; import { Grid3DLayer, MapLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; @@ -28,7 +29,6 @@ import { createSeismicCrosslineLayerData, createSeismicInlineLayerData } from ". import { RealizationGridLayer } from "../../LayerFramework/customLayerImplementations/RealizationGridLayer"; import { RealizationSurfaceLayer } from "../../LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { AdvancedWellsLayer } from "../customDeckGlLayers/AdvancedWellsLayer"; -import { WellBorePickLayerData, WellborePicksLayer } from "../customDeckGlLayers/WellborePicksLayer"; export function makeDeckGlLayer(layer: LayerInterface, colorScale?: ColorScaleWithName): Layer | null { const data = layer.getLayerDelegate().getData(); diff --git a/frontend/src/modules/_shared/LayerFramework/layers/LayerVisualizationFactory.ts b/frontend/src/modules/_shared/LayerFramework/layers/LayerVisualizationFactory.ts new file mode 100644 index 000000000..d9eddcd74 --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/layers/LayerVisualizationFactory.ts @@ -0,0 +1,250 @@ +import { Layer as DeckGlLayer } from "@deck.gl/core"; +import { Layer as EsvLayer } from "@equinor/esv-intersection"; +import { StatusMessage } from "@framework/ModuleInstanceStatusController"; +import { defaultColorPalettes, defaultContinuousSequentialColorPalettes } from "@framework/utils/colorPalettes"; +import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; +import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; +import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; + +import { GroupDelegate } from "../delegates/GroupDelegate"; +import { LayerColoringType, LayerStatus } from "../delegates/LayerDelegate"; +import { ColorScale } from "../framework/ColorScale/ColorScale"; +import { DeltaSurface } from "../framework/DeltaSurface/DeltaSurface"; +import { LayerManager } from "../framework/LayerManager/LayerManager"; +import { View } from "../framework/View/View"; +import { BoundingBox, Layer, instanceofGroup, instanceofLayer } from "../interfaces"; + +export enum LayerVisualizationTarget { + DECK_GL_2D = "deck_gl_2d", + DECK_GL_3D = "deck_gl_3d", + ESV = "esv", + // VIDEX = "videx", +} + +export type LayerVisualizationArgs = { + id: string; + name: string; + data: T; + colorScale: ColorScaleWithName; +}; + +export type TargetReturnTypes = { + [LayerVisualizationTarget.DECK_GL_2D]: DeckGlLayer; + [LayerVisualizationTarget.DECK_GL_3D]: DeckGlLayer; + [LayerVisualizationTarget.ESV]: EsvLayer; +}; + +export type MakeVisualizationFunction = ( + args: LayerVisualizationArgs +) => TargetReturnTypes[TTarget]; + +export type LayerWithPosition = { + layer: TargetReturnTypes[TTarget]; + position: number; +}; + +export type VisualizationView = { + id: string; + color: string | null; + name: string; + layers: LayerWithPosition[]; + colorScales: ColorScaleWithId[]; +}; + +export type FactoryProduct = { + views: VisualizationView[]; + layers: LayerWithPosition[]; + errorMessages: (StatusMessage | string)[]; + boundingBox: BoundingBox | null; + colorScales: ColorScaleWithId[]; + numLoadingLayers: number; +}; + +export class LayerVisualizationFactory { + private _layerManager: LayerManager; + private _visualizationFunctions: Map> = new Map(); + + constructor(layerManager: LayerManager) { + this._layerManager = layerManager; + } + + registerVisualizationFunctions( + funcs: { layer: Layer; func: MakeVisualizationFunction }[] + ): void { + for (const { layer, func } of funcs) { + this._visualizationFunctions.set(layer.constructor.name, func); + } + } + + make(): FactoryProduct { + return this.makeRecursively(this._layerManager.getGroupDelegate()); + } + + private makeRecursively(groupDelegate: GroupDelegate, numCollectedLayers: number = 0): FactoryProduct { + const collectedViews: VisualizationView[] = []; + const collectedLayers: LayerWithPosition[] = []; + const collectedColorScales: ColorScaleWithId[] = []; + const collectedErrorMessages: (StatusMessage | string)[] = []; + let collectedNumLoadingLayers = 0; + let globalBoundingBox: BoundingBox | null = null; + + const children = groupDelegate.getChildren(); + + const maybeApplyBoundingBox = (boundingBox: BoundingBox | null) => { + if (boundingBox) { + globalBoundingBox = + globalBoundingBox === null ? boundingBox : this.makeNewBoundingBox(boundingBox, globalBoundingBox); + } + }; + + for (const child of children) { + if (!child.getItemDelegate().isVisible()) { + continue; + } + + if (instanceofGroup(child) && !(child instanceof DeltaSurface)) { + const { views, layers, boundingBox, colorScales, numLoadingLayers, errorMessages } = + this.makeRecursively(child.getGroupDelegate(), numCollectedLayers + collectedLayers.length); + + collectedErrorMessages.push(...errorMessages); + collectedNumLoadingLayers += numLoadingLayers; + maybeApplyBoundingBox(boundingBox); + + if (child instanceof View) { + const view: VisualizationView = { + id: child.getItemDelegate().getId(), + color: child.getGroupDelegate().getColor(), + name: child.getItemDelegate().getName(), + layers: layers, + colorScales, + }; + + collectedViews.push(view); + continue; + } + + collectedLayers.push(...layers); + collectedViews.push(...views); + } + + if (instanceofLayer(child)) { + if (child.getLayerDelegate().getStatus() === LayerStatus.LOADING) { + collectedNumLoadingLayers++; + } + + if (child.getLayerDelegate().getStatus() !== LayerStatus.SUCCESS) { + if (child.getLayerDelegate().getStatus() === LayerStatus.ERROR) { + const error = child.getLayerDelegate().getError(); + if (error) { + collectedErrorMessages.push(error); + } + } + continue; + } + + const colorScale = this.findColorScale(child); + + const layer = this.makeLayer(child, colorScale?.colorScale ?? undefined); + + if (!layer) { + continue; + } + + if (colorScale) { + collectedColorScales.push(colorScale); + } + + const boundingBox = child.getLayerDelegate().getBoundingBox(); + maybeApplyBoundingBox(boundingBox); + collectedLayers.push({ layer, position: numCollectedLayers + collectedLayers.length }); + } + } + + return { + views: collectedViews, + layers: collectedLayers, + errorMessages: collectedErrorMessages, + boundingBox: globalBoundingBox, + colorScales: collectedColorScales, + numLoadingLayers: collectedNumLoadingLayers, + }; + } + + private makeLayer(layer: Layer, colorScale?: ColorScaleWithName): TargetReturnTypes[TTarget] { + const func = this._visualizationFunctions.get(layer.constructor.name); + if (!func) { + throw new Error(`No visualization function found for layer ${layer.constructor.name}`); + } + + if (colorScale === undefined) { + colorScale = new ColorScaleWithName({ + colorPalette: defaultColorPalettes[0], + gradientType: ColorScaleGradientType.Sequential, + name: "Default", + type: ColorScaleType.Continuous, + steps: 10, + }); + } + + return func({ + id: layer.getItemDelegate().getId(), + name: layer.getItemDelegate().getName(), + data: layer.getLayerDelegate().getData(), + colorScale, + }); + } + + private makeNewBoundingBox(newBoundingBox: BoundingBox, oldBoundingBox: BoundingBox): BoundingBox { + return { + x: [Math.min(newBoundingBox.x[0], oldBoundingBox.x[0]), Math.max(newBoundingBox.x[1], oldBoundingBox.x[1])], + y: [Math.min(newBoundingBox.y[0], oldBoundingBox.y[0]), Math.max(newBoundingBox.y[1], oldBoundingBox.y[1])], + z: [Math.min(newBoundingBox.z[0], oldBoundingBox.z[0]), Math.max(newBoundingBox.z[1], oldBoundingBox.z[1])], + }; + } + + private findColorScale(layer: Layer): { id: string; colorScale: ColorScaleWithName } | null { + if (layer.getLayerDelegate().getColoringType() !== LayerColoringType.COLORSCALE) { + return null; + } + + let colorScaleWithName = new ColorScaleWithName({ + colorPalette: defaultContinuousSequentialColorPalettes[0], + gradientType: ColorScaleGradientType.Sequential, + name: layer.getItemDelegate().getName(), + type: ColorScaleType.Continuous, + steps: 10, + }); + + const range = layer.getLayerDelegate().getValueRange(); + if (range) { + colorScaleWithName.setRangeAndMidPoint(range[0], range[1], (range[0] + range[1]) / 2); + } + + const colorScaleItemArr = layer + .getItemDelegate() + .getParentGroup() + ?.getAncestorAndSiblingItems((item) => item instanceof ColorScale); + + if (colorScaleItemArr && colorScaleItemArr.length > 0) { + const colorScaleItem = colorScaleItemArr[0]; + if (colorScaleItem instanceof ColorScale) { + colorScaleWithName = ColorScaleWithName.fromColorScale( + colorScaleItem.getColorScale(), + layer.getItemDelegate().getName() + ); + + if (!colorScaleItem.getAreBoundariesUserDefined()) { + const range = layer.getLayerDelegate().getValueRange(); + if (range) { + colorScaleWithName.setRangeAndMidPoint(range[0], range[1], (range[0] + range[1]) / 2); + } + } + } + } + + return { + id: layer.getItemDelegate().getId(), + colorScale: colorScaleWithName, + }; + } +} diff --git a/frontend/src/modules/2DViewer/view/customDeckGlLayers/WellborePicksLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts similarity index 100% rename from frontend/src/modules/2DViewer/view/customDeckGlLayers/WellborePicksLayer.ts rename to frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts From 6a511420ea25c5a2bff286e315e0a263b2b51b11 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 5 Feb 2025 18:18:25 +0100 Subject: [PATCH 33/97] wip --- .../2DViewer/view/utils/layerFactory.ts | 4 +- .../IntersectionRealizationGridLayer.ts | 157 +++--- .../RealizationGridLayer.ts | 37 +- .../RealizationSeismicCrosslineLayer.ts | 2 +- .../visualization/makeGrid3dLayer.ts | 37 ++ .../makeIntersectionGrid3dLayer.ts | 105 ++++ .../makeRealizationSeismicCrosslineLayer.ts | 115 ++++ .../view/components/LayersWrapper.tsx | 34 +- .../3DViewerNew/view/utils/layerFactory.ts | 498 ------------------ .../view/utils/makeViewsAndLayers.ts | 183 ------- .../VisualizationFactory.ts} | 50 +- .../deckgl/wellborePicksLayer.ts | 26 + .../visualization/deckgl/wellsLayer.ts | 82 +++ .../visualization/utils/colors.ts | 24 + .../customDeckGlLayers/AdvancedWellsLayer.ts | 4 - .../customDeckGlLayers/PlaceholderLayer.ts | 0 .../SeismicFenceMeshLayer.ts | 75 +++ .../customDeckGlLayers/WellborePicksLayer.ts | 4 +- 18 files changed, 596 insertions(+), 841 deletions(-) create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts delete mode 100644 frontend/src/modules/3DViewerNew/view/utils/makeViewsAndLayers.ts rename frontend/src/modules/_shared/LayerFramework/{layers/LayerVisualizationFactory.ts => visualization/VisualizationFactory.ts} (85%) create mode 100644 frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer.ts create mode 100644 frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellsLayer.ts create mode 100644 frontend/src/modules/_shared/LayerFramework/visualization/utils/colors.ts rename frontend/src/modules/{3DViewerNew/view => _shared}/customDeckGlLayers/AdvancedWellsLayer.ts (97%) rename frontend/src/modules/{3DViewerNew/view => _shared}/customDeckGlLayers/PlaceholderLayer.ts (100%) create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts diff --git a/frontend/src/modules/2DViewer/view/utils/layerFactory.ts b/frontend/src/modules/2DViewer/view/utils/layerFactory.ts index a39232530..9622371e8 100644 --- a/frontend/src/modules/2DViewer/view/utils/layerFactory.ts +++ b/frontend/src/modules/2DViewer/view/utils/layerFactory.ts @@ -9,7 +9,7 @@ import { Layer as LayerInterface } from "@modules/_shared/LayerFramework/interfa import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; -import { WellBorePickLayerData, WellborePicksLayer } from "@modules_shared/customDeckGlLayers/WellborePicksLayer"; +import { WellborePickLayerData, WellborePicksLayer } from "@modules_shared/customDeckGlLayers/WellborePicksLayer"; import { ColormapLayer, Grid3DLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; import { Rgb, parse } from "culori"; @@ -83,7 +83,7 @@ export function makeDeckGlLayer(layer: LayerInterface, colorScale?: Co return null; } function createWellPicksLayer(wellPicksDataApi: WellborePick_api[], id: string): WellborePicksLayer { - const wellPicksData: WellBorePickLayerData[] = wellPicksDataApi.map((wellPick) => { + const wellPicksData: WellborePickLayerData[] = wellPicksDataApi.map((wellPick) => { return { easting: wellPick.easting, northing: wellPick.northing, diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts index b4251b9b5..52832fee4 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts @@ -19,20 +19,9 @@ import { IntersectionRealizationGridSettingsContext } from "./IntersectionRealiz import { IntersectionRealizationGridSettings } from "./types"; export class IntersectionRealizationGridLayer - implements - Layer< - IntersectionRealizationGridSettings, - { - polylineIntersectionData: PolylineIntersection_trans | null; - } - > + implements Layer { - private _layerDelegate: LayerDelegate< - IntersectionRealizationGridSettings, - { - polylineIntersectionData: PolylineIntersection_trans | null; - } - >; + private _layerDelegate: LayerDelegate; private _itemDelegate: ItemDelegate; constructor(layerManager: LayerManager) { @@ -53,12 +42,7 @@ export class IntersectionRealizationGridLayer return this._itemDelegate; } - getLayerDelegate(): LayerDelegate< - IntersectionRealizationGridSettings, - { - polylineIntersectionData: PolylineIntersection_trans | null; - } - > { + getLayerDelegate(): LayerDelegate { return this._layerDelegate; } @@ -75,7 +59,8 @@ export class IntersectionRealizationGridLayer return null; } - if (data.polylineIntersectionData) { + // TODO: Implement bounding box calculation + if (data) { return null; } @@ -88,19 +73,14 @@ export class IntersectionRealizationGridLayer return null; } - if (data.polylineIntersectionData) { - return [ - data.polylineIntersectionData.min_grid_prop_value, - data.polylineIntersectionData.max_grid_prop_value, - ]; + if (data) { + return [data.min_grid_prop_value, data.max_grid_prop_value]; } return null; } - fetchData(queryClient: QueryClient): Promise<{ - polylineIntersectionData: PolylineIntersection_trans | null; - }> { + fetchData(queryClient: QueryClient): Promise { const settings = this.getSettingsContext().getDelegate().getSettings(); const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); @@ -125,67 +105,67 @@ export class IntersectionRealizationGridLayer ]; this._layerDelegate.registerQueryKey(queryKey); - if (!intersection) { - return Promise.resolve({ - polylineIntersectionData: null, - }); - } - let makePolylinePromise: Promise = new Promise((resolve) => { - if (intersection.type === "wellbore") { - return queryClient - .fetchQuery({ - ...getWellTrajectoriesOptions({ - query: { - field_identifier: fieldIdentifier ?? "", - wellbore_uuids: [intersection.uuid], - }, - }), - }) - .then((data) => { - const path: number[][] = []; - for (const [index, northing] of data[0].northingArr.entries()) { - const easting = data[0].eastingArr[index]; - const tvd_msl = data[0].tvdMslArr[index]; - - path.push([easting, northing, tvd_msl]); - } - const offset = data[0].tvdMslArr[0]; - - const intersectionReferenceSystem = new IntersectionReferenceSystem(path); - intersectionReferenceSystem.offset = offset; - - const polylineUtmXy: number[] = []; - polylineUtmXy.push( - ...calcExtendedSimplifiedWellboreTrajectoryInXYPlane( - path, - 0, - 5 - ).simplifiedWellboreTrajectoryXy.flat() - ); - - resolve(polylineUtmXy); - }); - } else { - const intersectionPolyline = this._itemDelegate - .getLayerManager() - .getWorkbenchSession() - .getUserCreatedItems() - .getIntersectionPolylines() - .getPolyline(intersection.uuid); - if (!intersectionPolyline) { - resolve([]); - return; - } + resolve([]); + }); - const polylineUtmXy: number[] = []; - for (const point of intersectionPolyline.path) { - polylineUtmXy.push(point[0], point[1]); + if (intersection) { + makePolylinePromise = new Promise((resolve) => { + if (intersection.type === "wellbore") { + return queryClient + .fetchQuery({ + ...getWellTrajectoriesOptions({ + query: { + field_identifier: fieldIdentifier ?? "", + wellbore_uuids: [intersection.uuid], + }, + }), + }) + .then((data) => { + const path: number[][] = []; + for (const [index, northing] of data[0].northingArr.entries()) { + const easting = data[0].eastingArr[index]; + const tvd_msl = data[0].tvdMslArr[index]; + + path.push([easting, northing, tvd_msl]); + } + const offset = data[0].tvdMslArr[0]; + + const intersectionReferenceSystem = new IntersectionReferenceSystem(path); + intersectionReferenceSystem.offset = offset; + + const polylineUtmXy: number[] = []; + polylineUtmXy.push( + ...calcExtendedSimplifiedWellboreTrajectoryInXYPlane( + path, + 0, + 5 + ).simplifiedWellboreTrajectoryXy.flat() + ); + + resolve(polylineUtmXy); + }); + } else { + const intersectionPolyline = this._itemDelegate + .getLayerManager() + .getWorkbenchSession() + .getUserCreatedItems() + .getIntersectionPolylines() + .getPolyline(intersection.uuid); + if (!intersectionPolyline) { + resolve([]); + return; + } + + const polylineUtmXy: number[] = []; + for (const point of intersectionPolyline.path) { + polylineUtmXy.push(point[0], point[1]); + } + + resolve(polylineUtmXy); } - - resolve(polylineUtmXy); - } - }); + }); + } const gridIntersectionPromise = makePolylinePromise .then((polyline_utm_xy) => @@ -203,10 +183,7 @@ export class IntersectionRealizationGridLayer }), }) ) - .then(transformPolylineIntersection) - .then((data) => ({ - polylineIntersectionData: data, - })); + .then(transformPolylineIntersection); return gridIntersectionPromise; } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts index ce1d84be9..8075ff4ea 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -18,23 +18,13 @@ import { isEqual } from "lodash"; import { RealizationGridSettingsContext } from "./RealizationGridSettingsContext"; import { RealizationGridSettings } from "./types"; -export class RealizationGridLayer - implements - Layer< - RealizationGridSettings, - { - gridSurfaceData: GridSurface_trans | null; - gridParameterData: GridMappedProperty_trans | null; - } - > -{ - private _layerDelegate: LayerDelegate< - RealizationGridSettings, - { - gridSurfaceData: GridSurface_trans | null; - gridParameterData: GridMappedProperty_trans | null; - } - >; +export type RealizationGridLayerData = { + gridSurfaceData: GridSurface_trans; + gridParameterData: GridMappedProperty_trans; +}; + +export class RealizationGridLayer implements Layer { + private _layerDelegate: LayerDelegate; private _itemDelegate: ItemDelegate; constructor(layerManager: LayerManager) { @@ -55,13 +45,7 @@ export class RealizationGridLayer return this._itemDelegate; } - getLayerDelegate(): LayerDelegate< - RealizationGridSettings, - { - gridSurfaceData: GridSurface_trans | null; - gridParameterData: GridMappedProperty_trans | null; - } - > { + getLayerDelegate(): LayerDelegate { return this._layerDelegate; } @@ -108,10 +92,7 @@ export class RealizationGridLayer return null; } - fetchData(queryClient: QueryClient): Promise<{ - gridSurfaceData: GridSurface_trans | null; - gridParameterData: GridMappedProperty_trans | null; - }> { + fetchData(queryClient: QueryClient): Promise { const settings = this.getSettingsContext().getDelegate().getSettings(); const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index eaaee0f87..f40543658 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -77,7 +77,7 @@ export class RealizationSeismicCrosslineLayer const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); const seismicCrosslineNumber = settings[SettingType.SEISMIC_CROSSLINE].getDelegate().getValue(); const queryKey = [ diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer.ts new file mode 100644 index 000000000..a34c34645 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer.ts @@ -0,0 +1,37 @@ +import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; +import { Grid3DLayer } from "@webviz/subsurface-viewer/dist/layers"; + +import { RealizationGridLayerData } from "../customLayerImplementations/RealizationGridLayer/RealizationGridLayer"; +import { RealizationGridSettings } from "../customLayerImplementations/RealizationGridLayer/types"; + +export function makeGrid3DLayer({ + id, + data, + settings, + colorScale, +}: VisualizationFunctionArgs): Grid3DLayer { + const { gridSurfaceData, gridParameterData } = data; + const offsetXyz = [gridSurfaceData.origin_utm_x, gridSurfaceData.origin_utm_y, 0]; + const pointsData = gridSurfaceData.pointsFloat32Arr.map((val, i) => val + offsetXyz[i % 3]); + const polysData = gridSurfaceData.polysUint32Arr; + + return new Grid3DLayer({ + id, + pointsData, + polysData, + propertiesData: gridParameterData.polyPropsFloat32Arr, + ZIncreasingDownwards: false, + gridLines: settings.showGridLines, + material: { ambient: 0.4, diffuse: 0.7, shininess: 8, specularColor: [25, 25, 25] }, + pickable: true, + colorMapName: "Physics", + colorMapClampColor: true, + colorMapRange: [gridParameterData.min_grid_prop_value, gridParameterData.max_grid_prop_value], + colorMapFunction: makeColorMapFunctionFromColorScale( + colorScale, + gridParameterData.min_grid_prop_value, + gridParameterData.max_grid_prop_value + ), + }); +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts new file mode 100644 index 000000000..201eb9174 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts @@ -0,0 +1,105 @@ +import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; +import { FenceMeshSection_trans, PolylineIntersection_trans } from "@modules/_shared/utils/wellbore"; +import { TGrid3DColoringMode } from "@webviz/subsurface-viewer"; +import { Grid3DLayer } from "@webviz/subsurface-viewer/dist/layers"; + +import { IntersectionRealizationGridSettings } from "../customLayerImplementations/IntersectionRealizationGridLayer/types"; + +interface PolyDataVtk { + points: Float32Array; + polys: Uint32Array; + props: Float32Array; +} + +function buildVtkStylePolyDataFromFenceSections(fenceSections: FenceMeshSection_trans[]): PolyDataVtk { + // Calculate sizes of typed arrays + let totNumVertices = 0; + let totNumPolygons = 0; + let totNumConnectivities = 0; + for (const section of fenceSections) { + totNumVertices += section.verticesUzFloat32Arr.length / 2; + totNumPolygons += section.verticesPerPolyUintArr.length; + totNumConnectivities += section.polyIndicesUintArr.length; + } + + const pointsFloat32Arr = new Float32Array(3 * totNumVertices); + const polysUint32Arr = new Uint32Array(totNumPolygons + totNumConnectivities); + const polyPropsFloat32Arr = new Float32Array(totNumPolygons); + + let floatPointsDstIdx = 0; + let polysDstIdx = 0; + let propsDstIdx = 0; + for (const section of fenceSections) { + // uv to xyz + const directionX = section.end_utm_x - section.start_utm_x; + const directionY = section.end_utm_y - section.start_utm_y; + const magnitude = Math.sqrt(directionX ** 2 + directionY ** 2); + const unitDirectionX = directionX / magnitude; + const unitDirectionY = directionY / magnitude; + + const connOffset = floatPointsDstIdx / 3; + + for (let i = 0; i < section.verticesUzFloat32Arr.length; i += 2) { + const u = section.verticesUzFloat32Arr[i]; + const z = section.verticesUzFloat32Arr[i + 1]; + const x = u * unitDirectionX + section.start_utm_x; + const y = u * unitDirectionY + section.start_utm_y; + + pointsFloat32Arr[floatPointsDstIdx++] = x; + pointsFloat32Arr[floatPointsDstIdx++] = y; + pointsFloat32Arr[floatPointsDstIdx++] = z; + } + + // Fix poly indexes for each section + const numPolysInSection = section.verticesPerPolyUintArr.length; + let srcIdx = 0; + for (let i = 0; i < numPolysInSection; i++) { + const numVertsInPoly = section.verticesPerPolyUintArr[i]; + polysUint32Arr[polysDstIdx++] = numVertsInPoly; + + for (let j = 0; j < numVertsInPoly; j++) { + polysUint32Arr[polysDstIdx++] = section.polyIndicesUintArr[srcIdx++] + connOffset; + } + } + + polyPropsFloat32Arr.set(section.polyPropsFloat32Arr, propsDstIdx); + propsDstIdx += numPolysInSection; + } + + return { + points: pointsFloat32Arr, + polys: polysUint32Arr, + props: polyPropsFloat32Arr, + }; +} + +export function makeIntersectionLayer({ + id, + data, + colorScale, + settings, +}: VisualizationFunctionArgs): Grid3DLayer { + const polyData = buildVtkStylePolyDataFromFenceSections(data.fenceMeshSections); + + const grid3dIntersectionLayer = new Grid3DLayer({ + id, + pointsData: polyData.points, + polysData: polyData.polys, + propertiesData: polyData.props, + colorMapName: "Continuous", + colorMapRange: [data.min_grid_prop_value, data.max_grid_prop_value], + colorMapClampColor: true, + coloringMode: TGrid3DColoringMode.Property, + colorMapFunction: makeColorMapFunctionFromColorScale( + colorScale, + data.min_grid_prop_value, + data.max_grid_prop_value + ), + ZIncreasingDownwards: false, + gridLines: settings.showGridLines, + material: { ambient: 0.4, diffuse: 0.7, shininess: 8, specularColor: [25, 25, 25] }, + pickable: false, + }); + return grid3dIntersectionLayer; +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts new file mode 100644 index 000000000..0f279b28e --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts @@ -0,0 +1,115 @@ +import { SeismicCrosslineData_trans } from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; +import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; +import { SeismicFenceMeshLayer } from "@modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer"; + +function generatePointFenceMesh( + startPoint: number[], + endPoint: number[], + numSamplesXY: number, + minDepth: number, + maxDepth: number, + numDepthSamples: number, + propertyArray: Float32Array +): { + vertices: Float32Array; + indices: Uint16Array; + properties: Float32Array; + minPropValue: number; + maxPropValue: number; +} { + const vertices = new Float32Array(numSamplesXY * numDepthSamples * 3); + const properties = new Float32Array(numSamplesXY * numDepthSamples); + + // Number of triangles to be drawn + const numTriangles = (numSamplesXY - 1) * (numDepthSamples - 1) * 2; + + // Triangle strip indices + const indices = new Uint16Array(numTriangles * 3); + + const stepX = (endPoint[0] - startPoint[0]) / (numSamplesXY - 1); + const stepY = (endPoint[1] - startPoint[1]) / (numSamplesXY - 1); + const stepZ = (maxDepth - minDepth) / (numDepthSamples - 1); + + let verticesIndex = 0; + let indicesIndex = 0; + let propertiesIndex = 0; + let minPropValue = Infinity; + let maxPropValue = -Infinity; + + for (let i = 0; i < numDepthSamples; i++) { + /* + if (i > 1) { + // Draw a degenerated triangle to move to the next row + indices[indicesIndex++] = (i - 1) * numSamplesXY + numSamplesXY - 1; + indices[indicesIndex++] = i * numSamplesXY; + } + */ + for (let j = 0; j < numSamplesXY; j++) { + vertices[verticesIndex++] = startPoint[0] + j * stepX; + vertices[verticesIndex++] = startPoint[1] + j * stepY; + vertices[verticesIndex++] = -(minDepth + i * stepZ); + + const propValue = propertyArray[j * numDepthSamples + i]; + properties[propertiesIndex++] = propValue; + minPropValue = Math.min(minPropValue, propValue); + maxPropValue = Math.max(maxPropValue, propValue); + + /* + if (i > 0) { + indices[indicesIndex++] = (i - 1) * numSamplesXY + j; + indices[indicesIndex++] = i * numSamplesXY + j; + } + */ + + if (i > 0 && j > 0) { + // Three indices for the triangle + indices[indicesIndex++] = (i - 1) * numSamplesXY + j; + indices[indicesIndex++] = i * numSamplesXY + j; + indices[indicesIndex++] = (i - 1) * numSamplesXY + j + 1; + + // Three indices for the triangle + indices[indicesIndex++] = i * numSamplesXY + j; + indices[indicesIndex++] = i * numSamplesXY + j + 1; + indices[indicesIndex++] = (i - 1) * numSamplesXY + j + 1; + } + } + } + + return { vertices, indices, properties, minPropValue, maxPropValue }; +} + +export function makeRealizationSeismicCrosslineLayer({ + id, + data, + colorScale, +}: VisualizationFunctionArgs): SeismicFenceMeshLayer { + const startPoint = [data.start_utm_x, data.start_utm_y]; + const endPoint = [data.end_utm_x, data.end_utm_y]; + const numSamples = data.inline_no_samples; + const minDepth = data.z_min; + const maxDepth = data.z_max; + const numDepthSamples = data.z_samples; + const propertyArray = data.dataFloat32Arr; + + const { vertices, indices, properties, minPropValue, maxPropValue } = generatePointFenceMesh( + startPoint, + endPoint, + numSamples, + minDepth, + maxDepth, + numDepthSamples, + propertyArray + ); + + return new SeismicFenceMeshLayer({ + id, + data: { + vertices, + indices, + properties, + }, + propertyRange: [minPropValue, maxPropValue], + colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, minPropValue, maxPropValue, false), + }); +} diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index bb527063f..8c6b1795f 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -10,8 +10,23 @@ import { useElementSize } from "@lib/hooks/useElementSize"; import { Rect3D, outerRectContainsInnerRect } from "@lib/utils/geometry"; import { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; +import { IntersectionRealizationGridLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer"; +import { RealizationGridLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer"; +import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; +import { makeGrid3DLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer"; +import { makeIntersectionLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer"; +import { makeRealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer"; import { LayerManager, LayerManagerTopic } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { BoundingBox } from "@modules/_shared/LayerFramework/interfaces"; +import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; +import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; +import { + LayerWithPosition, + VisualizationFactory, + VisualizationTarget, +} from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import { makeWellborePicksLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer"; +import { makeWellsLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/wellsLayer"; import { ColorLegendsContainer } from "@modules/_shared/components/ColorLegendsContainer"; import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; @@ -21,8 +36,17 @@ import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; import { ReadoutWrapper } from "./ReadoutWrapper"; -import { PlaceholderLayer } from "../customDeckGlLayers/PlaceholderLayer"; -import { DeckGlLayerWithPosition, recursivelyMakeViewsAndLayers } from "../utils/makeViewsAndLayers"; +import { PlaceholderLayer } from "../../../_shared/customDeckGlLayers/PlaceholderLayer"; + +const VISUALIZATION_FACTORY = new VisualizationFactory(); +VISUALIZATION_FACTORY.registerVisualizationFunction(DrilledWellborePicksLayer, makeWellborePicksLayer); +VISUALIZATION_FACTORY.registerVisualizationFunction(DrilledWellTrajectoriesLayer, makeWellsLayer); +VISUALIZATION_FACTORY.registerVisualizationFunction(RealizationGridLayer, makeGrid3DLayer); +VISUALIZATION_FACTORY.registerVisualizationFunction(IntersectionRealizationGridLayer, makeIntersectionLayer); +VISUALIZATION_FACTORY.registerVisualizationFunction( + RealizationSeismicCrosslineLayer, + makeRealizationSeismicCrosslineLayer +); export type LayersWrapperProps = { layerManager: LayerManager; @@ -42,7 +66,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { usePublishSubscribeTopicValue(props.layerManager, LayerManagerTopic.LAYER_DATA_REVISION); const viewports: ViewportType[] = []; - const viewerLayers: DeckGlLayerWithPosition[] = []; + const viewerLayers: LayerWithPosition[] = []; const viewportAnnotations: React.ReactNode[] = []; const globalColorScales: ColorScaleWithId[] = []; @@ -57,7 +81,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { let numLoadingLayers = 0; - const viewsAndLayers = recursivelyMakeViewsAndLayers(props.layerManager.getGroupDelegate()); + const viewsAndLayers = VISUALIZATION_FACTORY.make(props.layerManager); numCols = Math.ceil(Math.sqrt(viewsAndLayers.views.length)); numRows = Math.ceil(viewsAndLayers.views.length / numCols); @@ -161,7 +185,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { const layers = viewerLayers.toSorted((a, b) => b.position - a.position).map((layer) => layer.layer); layers.push(new PlaceholderLayer({ id: "placeholder" })); - layers.push(new AxesLayer({ id: "axes-layer", visible: true, ZIncreasingDownwards: false, bounds })); + layers.push(new AxesLayer({ id: "axes-layer", visible: true, ZIncreasingDownwards: true, bounds })); return (
diff --git a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts b/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts deleted file mode 100644 index 63f99ebe8..000000000 --- a/frontend/src/modules/3DViewerNew/view/utils/layerFactory.ts +++ /dev/null @@ -1,498 +0,0 @@ -import { SurfaceDef_api, WellborePick_api, WellboreTrajectory_api } from "@api"; -import { Layer } from "@deck.gl/core"; -import { defaultColorPalettes } from "@framework/utils/colorPalettes"; -import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; -import { GridMappedProperty_trans, GridSurface_trans } from "@modules/3DViewer/view/queries/queryDataTransforms"; -import { IntersectionRealizationGridLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer"; -import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; -import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer"; -import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; -import { - SeismicCrosslineData_trans, - SeismicInlineData_trans, -} from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; -import { Layer as LayerInterface } from "@modules/_shared/LayerFramework/interfaces"; -import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; -import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; -import { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataTransforms"; -import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; -import { FenceMeshSection_trans, PolylineIntersection_trans } from "@modules/_shared/utils/wellbore"; -import { WellBorePickLayerData, WellborePicksLayer } from "@modules_shared/customDeckGlLayers/WellborePicksLayer"; -import { TGrid3DColoringMode } from "@webviz/subsurface-viewer"; -import { Grid3DLayer, MapLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; - -import { Rgb, parse } from "culori"; -import { Feature } from "geojson"; - -import { createSeismicCrosslineLayerData, createSeismicInlineLayerData } from "./seismicSliceUtils"; - -import { RealizationGridLayer } from "../../LayerFramework/customLayerImplementations/RealizationGridLayer"; -import { RealizationSurfaceLayer } from "../../LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; -import { AdvancedWellsLayer } from "../customDeckGlLayers/AdvancedWellsLayer"; - -export function makeDeckGlLayer(layer: LayerInterface, colorScale?: ColorScaleWithName): Layer | null { - const data = layer.getLayerDelegate().getData(); - - if (colorScale === undefined) { - colorScale = new ColorScaleWithName({ - colorPalette: defaultColorPalettes[0], - gradientType: ColorScaleGradientType.Sequential, - name: "Default", - type: ColorScaleType.Continuous, - steps: 10, - }); - } - - if (!data) { - return null; - } - if (layer instanceof DrilledWellTrajectoriesLayer) { - return makeWellsLayer(data, layer.getItemDelegate().getId(), null); - } - if (layer instanceof DrilledWellborePicksLayer) { - return createWellPicksLayer(data, layer.getItemDelegate().getId()); - } - if (layer instanceof IntersectionRealizationGridLayer) { - return makeIntersectionLayer( - data.polylineIntersectionData, - layer.getSettingsContext().getDelegate().getSettings().showGridLines.getDelegate().getValue(), - colorScale - ); - } - if (layer instanceof RealizationGridLayer) { - return makeGrid3DLayer( - layer.getItemDelegate().getId(), - data.gridSurfaceData, - data.gridParameterData, - layer.getSettingsContext().getDelegate().getSettings().showGridLines.getDelegate().getValue(), - colorScale - ); - } - if (layer instanceof RealizationSurfaceLayer) { - return createSurfaceMeshLayer( - data, - layer.getItemDelegate().getId(), - layer.getItemDelegate().getName(), - colorScale - ); - } - if (layer instanceof RealizationSeismicInlineLayer) { - const seismicData: SeismicInlineData_trans = data; - const seismicLayerData = createSeismicInlineLayerData(seismicData); - - const coloringMode: TGrid3DColoringMode = TGrid3DColoringMode.Property; - const grid3dLayer = new Grid3DLayer({ - id: layer.getItemDelegate().getId(), - name: layer.getItemDelegate().getName(), - pointsData: seismicLayerData.pointsFloat32Arr, - polysData: seismicLayerData.polysUint32Arr, - propertiesData: seismicLayerData.propertyFloat32Arr, - ZIncreasingDownwards: true, - gridLines: false, - colorMapRange: [seismicLayerData.minValue, seismicLayerData.maxValue], - colorMapName: "Physics", - colorMapClampColor: true, - coloringMode: coloringMode, - material: { ambient: 0, diffuse: 0.7, shininess: 1, specularColor: [25, 25, 25] }, - colorMapFunction: makeColorMapFunction(colorScale, seismicLayerData.minValue, seismicLayerData.maxValue), - - pickable: true, - }); - return grid3dLayer as unknown as WorkingGrid3dLayer; - } - if (layer instanceof RealizationSeismicCrosslineLayer) { - const seismicData: SeismicCrosslineData_trans = data; - const seismicLayerData = createSeismicCrosslineLayerData(seismicData); - - const coloringMode: TGrid3DColoringMode = TGrid3DColoringMode.Property; - const grid3dLayer = new Grid3DLayer({ - id: layer.getItemDelegate().getId(), - name: layer.getItemDelegate().getName(), - pointsData: seismicLayerData.pointsFloat32Arr, - polysData: seismicLayerData.polysUint32Arr, - propertiesData: seismicLayerData.propertyFloat32Arr, - ZIncreasingDownwards: true, - gridLines: false, - colorMapRange: [seismicLayerData.minValue, seismicLayerData.maxValue], - colorMapName: "Physics", - colorMapClampColor: true, - coloringMode: coloringMode, - material: { ambient: 0, diffuse: 0.7, shininess: 1, specularColor: [25, 25, 25] }, - colorMapFunction: makeColorMapFunction(colorScale, seismicLayerData.minValue, seismicLayerData.maxValue), - - pickable: true, - }); - return grid3dLayer as unknown as WorkingGrid3dLayer; - } - if (layer instanceof RealizationSeismicDepthSliceLayer) { - const zValue = layer - .getSettingsContext() - .getDelegate() - .getSettings() - .seismicDepthSlice.getDelegate() - .getValue(); - const layerData: SurfaceDataFloat_trans = data; - return createSurfaceConstantZWIthPropertyLayer({ - surfaceDef: layerData.surface_def, - propertyFloat32Arr: layerData.valuesFloat32Arr, - zValue: zValue || 0, - id: layer.getItemDelegate().getId(), - name: layer.getItemDelegate().getName(), - valueMin: layerData.value_min, - valueMax: layerData.value_max, - colorScale: colorScale, - showGridLines: false, - }); - } - return null; -} - -export function createSurfaceMeshLayer( - layerData: SurfaceDataFloat_trans, - id: string, - name: string, - colorScale?: ColorScaleWithName, - showContours?: boolean | number[], - showGridLines?: boolean, - useSmoothShading?: boolean, - useMaterial?: boolean, - property_data?: Float32Array | null -): MapLayer { - return new MapLayer({ - "@@type": "MapLayer", - "@@typedArraySupport": true, - id: "mesh-layer", - name: name, - meshData: layerData.valuesFloat32Arr, - // propertiesData: layerData.valuesFloat32Arr, - frame: { - origin: [layerData.surface_def.origin_utm_x, layerData.surface_def.origin_utm_y], - count: [layerData.surface_def.npoints_x, layerData.surface_def.npoints_y], - increment: [layerData.surface_def.inc_x, layerData.surface_def.inc_y], - rotDeg: layerData.surface_def.rot_deg, - }, - valueRange: [layerData.value_min, layerData.value_max], - colorMapRange: [layerData.value_min, layerData.value_max], - // isContoursDepth: true, - // contours: [0, 10], - gridLines: false, - material: useMaterial, - smoothShading: useSmoothShading, - colorMapName: "Physics", - - colorMapFunction: makeColorMapFunction(colorScale, layerData.value_min, layerData.value_max), - }); -} -type SurfaceConstantZWithPropertyLayerOptions = { - surfaceDef: SurfaceDef_api; - propertyFloat32Arr: Float32Array; - zValue: number; - id: string; - name: string; - valueMin: number; - valueMax: number; - colorScale: ColorScaleWithName; - showGridLines: boolean; -}; -export function createSurfaceConstantZWIthPropertyLayer(options: SurfaceConstantZWithPropertyLayerOptions): MapLayer { - const meshData = new Float32Array(options.propertyFloat32Arr.length); - meshData.fill(options.zValue); - return new MapLayer({ - "@@type": "MapLayer", - "@@typedArraySupport": true, - id: options.id, - name: options.name, - meshData: meshData, - propertiesData: options.propertyFloat32Arr, - frame: { - origin: [options.surfaceDef.origin_utm_x, options.surfaceDef.origin_utm_y], - count: [options.surfaceDef.npoints_x, options.surfaceDef.npoints_y], - increment: [options.surfaceDef.inc_x, options.surfaceDef.inc_y], - rotDeg: options.surfaceDef.rot_deg, - }, - valueRange: [options.valueMin, options.valueMax], - colorMapRange: [options.valueMin, options.valueMax], - gridLines: options.showGridLines, - material: false, - smoothShading: false, - colorMapName: "Physics", - - colorMapFunction: makeColorMapFunction(options.colorScale, options.valueMin, options.valueMax), - }); -} -function createWellPicksLayer(wellPicksDataApi: WellborePick_api[], id: string): WellborePicksLayer { - const wellPicksData: WellBorePickLayerData[] = wellPicksDataApi.map((wellPick) => { - return { - easting: wellPick.easting, - northing: wellPick.northing, - wellBoreUwi: wellPick.uniqueWellboreIdentifier, - tvdMsl: wellPick.tvdMsl, - md: wellPick.md, - pickable: true, - slotName: "", - }; - }); - return new WellborePicksLayer({ - id: id, - data: wellPicksData, - pickable: true, - }); -} - -function makeWellsLayer( - fieldWellboreTrajectoriesData: WellboreTrajectory_api[], - id: string, - selectedWellboreUuid: string | null -): WellsLayer { - const tempWorkingWellsData = fieldWellboreTrajectoriesData.filter( - (el) => el.uniqueWellboreIdentifier !== "NO 34/4-K-3 AH" - ); - const wellLayerDataFeatures = tempWorkingWellsData.map((well) => - wellTrajectoryToGeojson(well, selectedWellboreUuid) - ); - - function getLineStyleWidth(object: Feature): number { - if (object.properties && "lineWidth" in object.properties) { - return object.properties.lineWidth as number; - } - return 2; - } - - function getWellHeadStyleWidth(object: Feature): number { - if (object.properties && "wellHeadSize" in object.properties) { - return object.properties.wellHeadSize as number; - } - return 1; - } - - function getColor(object: Feature): [number, number, number, number] { - if (object.properties && "color" in object.properties) { - return object.properties.color as [number, number, number, number]; - } - return [50, 50, 50, 100]; - } - - const wellsLayer = new AdvancedWellsLayer({ - id: id, - data: { - type: "FeatureCollection", - unit: "m", - features: wellLayerDataFeatures, - }, - refine: false, - lineStyle: { width: getLineStyleWidth, color: getColor }, - wellHeadStyle: { size: getWellHeadStyleWidth, color: getColor }, - wellNameVisible: true, - pickable: true, - ZIncreasingDownwards: false, - outline: false, - lineWidthScale: 2, - }); - - return wellsLayer; -} - -function wellTrajectoryToGeojson( - wellTrajectory: WellboreTrajectory_api, - selectedWellboreUuid: string | null -): Record { - const point: Record = { - type: "Point", - coordinates: [wellTrajectory.eastingArr[0], wellTrajectory.northingArr[0], -wellTrajectory.tvdMslArr[0]], - }; - const coordinates: Record = { - type: "LineString", - coordinates: zipCoords(wellTrajectory.eastingArr, wellTrajectory.northingArr, wellTrajectory.tvdMslArr), - }; - - let color = [100, 100, 100]; - let lineWidth = 2; - let wellHeadSize = 1; - if (wellTrajectory.wellboreUuid === selectedWellboreUuid) { - color = [255, 0, 0]; - lineWidth = 5; - wellHeadSize = 10; - } - - const geometryCollection: Record = { - type: "Feature", - geometry: { - type: "GeometryCollection", - geometries: [point, coordinates], - }, - properties: { - uuid: wellTrajectory.wellboreUuid, - name: wellTrajectory.uniqueWellboreIdentifier, - uwi: wellTrajectory.uniqueWellboreIdentifier, - color, - md: [wellTrajectory.mdArr], - lineWidth, - wellHeadSize, - }, - }; - - return geometryCollection; -} - -function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { - const coords: number[][] = []; - for (let i = 0; i < xArr.length; i++) { - coords.push([xArr[i], yArr[i], -zArr[i]]); - } - - return coords; -} -type WorkingGrid3dLayer = { - pointsData: Float32Array; - polysData: Uint32Array; - propertiesData: Float32Array; - colorMapName: string; - ZIncreasingDownwards: boolean; -} & Layer; - -function makeGrid3DLayer( - id: string, - gridSurfaceData: GridSurface_trans, - gridParameterData: GridMappedProperty_trans, - showGridLines: boolean, - colorScale?: ColorScaleWithName -): WorkingGrid3dLayer { - const offsetXyz = [gridSurfaceData.origin_utm_x, gridSurfaceData.origin_utm_y, 0]; - const pointsNumberArray = gridSurfaceData.pointsFloat32Arr.map((val, i) => val + offsetXyz[i % 3]); - const polysNumberArray = gridSurfaceData.polysUint32Arr; - const grid3dLayer = new Grid3DLayer({ - id: id, - pointsData: pointsNumberArray, - polysData: polysNumberArray, - propertiesData: gridParameterData.polyPropsFloat32Arr, - ZIncreasingDownwards: false, - gridLines: showGridLines, - material: { ambient: 0.4, diffuse: 0.7, shininess: 8, specularColor: [25, 25, 25] }, - pickable: true, - colorMapName: "Physics", - colorMapClampColor: true, - colorMapRange: [gridParameterData.min_grid_prop_value, gridParameterData.max_grid_prop_value], - colorMapFunction: makeColorMapFunction( - colorScale, - gridParameterData.min_grid_prop_value, - gridParameterData.max_grid_prop_value - ), - }); - return grid3dLayer as unknown as WorkingGrid3dLayer; -} - -interface PolyDataVtk { - points: Float32Array; - polys: Uint32Array; - props: Float32Array; -} - -function buildVtkStylePolyDataFromFenceSections(fenceSections: FenceMeshSection_trans[]): PolyDataVtk { - // Calculate sizes of typed arrays - let totNumVertices = 0; - let totNumPolygons = 0; - let totNumConnectivities = 0; - for (const section of fenceSections) { - totNumVertices += section.verticesUzFloat32Arr.length / 2; - totNumPolygons += section.verticesPerPolyUintArr.length; - totNumConnectivities += section.polyIndicesUintArr.length; - } - - const pointsFloat32Arr = new Float32Array(3 * totNumVertices); - const polysUint32Arr = new Uint32Array(totNumPolygons + totNumConnectivities); - const polyPropsFloat32Arr = new Float32Array(totNumPolygons); - - let floatPointsDstIdx = 0; - let polysDstIdx = 0; - let propsDstIdx = 0; - for (const section of fenceSections) { - // uv to xyz - const directionX = section.end_utm_x - section.start_utm_x; - const directionY = section.end_utm_y - section.start_utm_y; - const magnitude = Math.sqrt(directionX ** 2 + directionY ** 2); - const unitDirectionX = directionX / magnitude; - const unitDirectionY = directionY / magnitude; - - const connOffset = floatPointsDstIdx / 3; - - for (let i = 0; i < section.verticesUzFloat32Arr.length; i += 2) { - const u = section.verticesUzFloat32Arr[i]; - const z = section.verticesUzFloat32Arr[i + 1]; - const x = u * unitDirectionX + section.start_utm_x; - const y = u * unitDirectionY + section.start_utm_y; - - pointsFloat32Arr[floatPointsDstIdx++] = x; - pointsFloat32Arr[floatPointsDstIdx++] = y; - pointsFloat32Arr[floatPointsDstIdx++] = z; - } - - // Fix poly indexes for each section - const numPolysInSection = section.verticesPerPolyUintArr.length; - let srcIdx = 0; - for (let i = 0; i < numPolysInSection; i++) { - const numVertsInPoly = section.verticesPerPolyUintArr[i]; - polysUint32Arr[polysDstIdx++] = numVertsInPoly; - - for (let j = 0; j < numVertsInPoly; j++) { - polysUint32Arr[polysDstIdx++] = section.polyIndicesUintArr[srcIdx++] + connOffset; - } - } - - polyPropsFloat32Arr.set(section.polyPropsFloat32Arr, propsDstIdx); - propsDstIdx += numPolysInSection; - } - - return { - points: pointsFloat32Arr, - polys: polysUint32Arr, - props: polyPropsFloat32Arr, - }; -} - -function makeIntersectionLayer( - polylineIntersectionData: PolylineIntersection_trans, - showGridLines: boolean, - colorScale: ColorScaleWithName -): WorkingGrid3dLayer { - const polyData = buildVtkStylePolyDataFromFenceSections(polylineIntersectionData.fenceMeshSections); - const grid3dIntersectionLayer = new Grid3DLayer({ - id: "grid-3d-intersection-layer", - pointsData: polyData.points as unknown as number[], - polysData: polyData.polys as unknown as number[], - propertiesData: polyData.props as unknown as number[], - colorMapName: "Continuous", - colorMapRange: [polylineIntersectionData.min_grid_prop_value, polylineIntersectionData.max_grid_prop_value], - colorMapClampColor: true, - coloringMode: TGrid3DColoringMode.Property, - colorMapFunction: makeColorMapFunction( - colorScale, - polylineIntersectionData.min_grid_prop_value, - polylineIntersectionData.max_grid_prop_value - ), - ZIncreasingDownwards: false, - gridLines: showGridLines, - material: { ambient: 0.4, diffuse: 0.7, shininess: 8, specularColor: [25, 25, 25] }, - pickable: false, - }); - return grid3dIntersectionLayer as unknown as WorkingGrid3dLayer; -} - -function makeColorMapFunction( - colorScale: ColorScaleWithName | undefined, - valueMin: number, - valueMax: number -): ((value: number) => [number, number, number]) | undefined { - if (!colorScale) { - return undefined; - } - - return (value: number) => { - const nonNormalizedValue = value * (valueMax - valueMin) + valueMin; - const interpolatedColor = colorScale.getColorForValue(nonNormalizedValue); - const color = parse(interpolatedColor) as Rgb; - if (color === undefined) { - return [0, 0, 0]; - } - return [color.r * 255, color.g * 255, color.b * 255]; - }; -} diff --git a/frontend/src/modules/3DViewerNew/view/utils/makeViewsAndLayers.ts b/frontend/src/modules/3DViewerNew/view/utils/makeViewsAndLayers.ts deleted file mode 100644 index 824c59cea..000000000 --- a/frontend/src/modules/3DViewerNew/view/utils/makeViewsAndLayers.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { Layer as DeckGlLayer } from "@deck.gl/core"; -import { StatusMessage } from "@framework/ModuleInstanceStatusController"; -import { defaultContinuousSequentialColorPalettes } from "@framework/utils/colorPalettes"; -import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; -import { GroupDelegate } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; -import { LayerColoringType, LayerStatus } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; -import { ColorScale } from "@modules/_shared/LayerFramework/framework/ColorScale/ColorScale"; -import { DeltaSurface } from "@modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurface"; -import { View } from "@modules/_shared/LayerFramework/framework/View/View"; -import { BoundingBox, Layer, instanceofGroup, instanceofLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; -import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; - -import { makeDeckGlLayer } from "./layerFactory"; - -export type DeckGlLayerWithPosition = { - layer: DeckGlLayer; - position: number; -}; - -export type DeckGlView = { - id: string; - color: string | null; - name: string; - layers: DeckGlLayerWithPosition[]; - colorScales: ColorScaleWithId[]; -}; - -export type DeckGlViewsAndLayers = { - views: DeckGlView[]; - layers: DeckGlLayerWithPosition[]; - errorMessages: (StatusMessage | string)[]; - boundingBox: BoundingBox | null; - colorScales: ColorScaleWithId[]; - numLoadingLayers: number; -}; - -export function recursivelyMakeViewsAndLayers( - groupDelegate: GroupDelegate, - numCollectedLayers: number = 0 -): DeckGlViewsAndLayers { - const collectedViews: DeckGlView[] = []; - const collectedLayers: DeckGlLayerWithPosition[] = []; - const collectedColorScales: ColorScaleWithId[] = []; - const collectedErrorMessages: (StatusMessage | string)[] = []; - let collectedNumLoadingLayers = 0; - let globalBoundingBox: BoundingBox | null = null; - - const children = groupDelegate.getChildren(); - - const maybeApplyBoundingBox = (boundingBox: BoundingBox | null) => { - if (boundingBox) { - globalBoundingBox = - globalBoundingBox === null ? boundingBox : makeNewBoundingBox(boundingBox, globalBoundingBox); - } - }; - - for (const child of children) { - if (!child.getItemDelegate().isVisible()) { - continue; - } - - if (instanceofGroup(child) && !(child instanceof DeltaSurface)) { - const { views, layers, boundingBox, colorScales, numLoadingLayers, errorMessages } = - recursivelyMakeViewsAndLayers(child.getGroupDelegate(), numCollectedLayers + collectedLayers.length); - - collectedErrorMessages.push(...errorMessages); - collectedNumLoadingLayers += numLoadingLayers; - maybeApplyBoundingBox(boundingBox); - - if (child instanceof View) { - const view: DeckGlView = { - id: child.getItemDelegate().getId(), - color: child.getGroupDelegate().getColor(), - name: child.getItemDelegate().getName(), - layers: layers, - colorScales, - }; - - collectedViews.push(view); - continue; - } - - collectedLayers.push(...layers); - collectedViews.push(...views); - } - - if (instanceofLayer(child)) { - if (child.getLayerDelegate().getStatus() === LayerStatus.LOADING) { - collectedNumLoadingLayers++; - } - - if (child.getLayerDelegate().getStatus() !== LayerStatus.SUCCESS) { - if (child.getLayerDelegate().getStatus() === LayerStatus.ERROR) { - const error = child.getLayerDelegate().getError(); - if (error) { - collectedErrorMessages.push(error); - } - } - continue; - } - - const colorScale = findColorScale(child); - - const layer = makeDeckGlLayer(child, colorScale?.colorScale ?? undefined); - - if (!layer) { - continue; - } - - if (colorScale) { - collectedColorScales.push(colorScale); - } - - const boundingBox = child.getLayerDelegate().getBoundingBox(); - maybeApplyBoundingBox(boundingBox); - collectedLayers.push({ layer, position: numCollectedLayers + collectedLayers.length }); - } - } - - return { - views: collectedViews, - layers: collectedLayers, - errorMessages: collectedErrorMessages, - boundingBox: globalBoundingBox, - colorScales: collectedColorScales, - numLoadingLayers: collectedNumLoadingLayers, - }; -} - -function findColorScale(layer: Layer): { id: string; colorScale: ColorScaleWithName } | null { - if (layer.getLayerDelegate().getColoringType() !== LayerColoringType.COLORSCALE) { - return null; - } - - let colorScaleWithName = new ColorScaleWithName({ - colorPalette: defaultContinuousSequentialColorPalettes[0], - gradientType: ColorScaleGradientType.Sequential, - name: layer.getItemDelegate().getName(), - type: ColorScaleType.Continuous, - steps: 10, - }); - - const range = layer.getLayerDelegate().getValueRange(); - if (range) { - colorScaleWithName.setRangeAndMidPoint(range[0], range[1], (range[0] + range[1]) / 2); - } - - const colorScaleItemArr = layer - .getItemDelegate() - .getParentGroup() - ?.getAncestorAndSiblingItems((item) => item instanceof ColorScale); - - if (colorScaleItemArr && colorScaleItemArr.length > 0) { - const colorScaleItem = colorScaleItemArr[0]; - if (colorScaleItem instanceof ColorScale) { - colorScaleWithName = ColorScaleWithName.fromColorScale( - colorScaleItem.getColorScale(), - layer.getItemDelegate().getName() - ); - - if (!colorScaleItem.getAreBoundariesUserDefined()) { - const range = layer.getLayerDelegate().getValueRange(); - if (range) { - colorScaleWithName.setRangeAndMidPoint(range[0], range[1], (range[0] + range[1]) / 2); - } - } - } - } - - return { - id: layer.getItemDelegate().getId(), - colorScale: colorScaleWithName, - }; -} - -function makeNewBoundingBox(newBoundingBox: BoundingBox, oldBoundingBox: BoundingBox): BoundingBox { - return { - x: [Math.min(newBoundingBox.x[0], oldBoundingBox.x[0]), Math.max(newBoundingBox.x[1], oldBoundingBox.x[1])], - y: [Math.min(newBoundingBox.y[0], oldBoundingBox.y[0]), Math.max(newBoundingBox.y[1], oldBoundingBox.y[1])], - z: [Math.min(newBoundingBox.z[0], oldBoundingBox.z[0]), Math.max(newBoundingBox.z[1], oldBoundingBox.z[1])], - }; -} diff --git a/frontend/src/modules/_shared/LayerFramework/layers/LayerVisualizationFactory.ts b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts similarity index 85% rename from frontend/src/modules/_shared/LayerFramework/layers/LayerVisualizationFactory.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts index d9eddcd74..22075410c 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/LayerVisualizationFactory.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts @@ -12,38 +12,37 @@ import { ColorScale } from "../framework/ColorScale/ColorScale"; import { DeltaSurface } from "../framework/DeltaSurface/DeltaSurface"; import { LayerManager } from "../framework/LayerManager/LayerManager"; import { View } from "../framework/View/View"; -import { BoundingBox, Layer, instanceofGroup, instanceofLayer } from "../interfaces"; +import { BoundingBox, Layer, Settings, instanceofGroup, instanceofLayer } from "../interfaces"; -export enum LayerVisualizationTarget { - DECK_GL_2D = "deck_gl_2d", - DECK_GL_3D = "deck_gl_3d", +export enum VisualizationTarget { + DECK_GL = "deck_gl_2d", ESV = "esv", // VIDEX = "videx", } -export type LayerVisualizationArgs = { +export type VisualizationFunctionArgs = { id: string; name: string; - data: T; + data: TData; colorScale: ColorScaleWithName; + settings: TSettings; }; export type TargetReturnTypes = { - [LayerVisualizationTarget.DECK_GL_2D]: DeckGlLayer; - [LayerVisualizationTarget.DECK_GL_3D]: DeckGlLayer; - [LayerVisualizationTarget.ESV]: EsvLayer; + [VisualizationTarget.DECK_GL]: DeckGlLayer; + [VisualizationTarget.ESV]: EsvLayer; }; -export type MakeVisualizationFunction = ( - args: LayerVisualizationArgs +export type MakeVisualizationFunction = ( + args: VisualizationFunctionArgs ) => TargetReturnTypes[TTarget]; -export type LayerWithPosition = { +export type LayerWithPosition = { layer: TargetReturnTypes[TTarget]; position: number; }; -export type VisualizationView = { +export type VisualizationView = { id: string; color: string | null; name: string; @@ -51,7 +50,7 @@ export type VisualizationView = { colorScales: ColorScaleWithId[]; }; -export type FactoryProduct = { +export type FactoryProduct = { views: VisualizationView[]; layers: LayerWithPosition[]; errorMessages: (StatusMessage | string)[]; @@ -60,24 +59,18 @@ export type FactoryProduct = { numLoadingLayers: number; }; -export class LayerVisualizationFactory { - private _layerManager: LayerManager; - private _visualizationFunctions: Map> = new Map(); +export class VisualizationFactory { + private _visualizationFunctions: Map> = new Map(); - constructor(layerManager: LayerManager) { - this._layerManager = layerManager; - } - - registerVisualizationFunctions( - funcs: { layer: Layer; func: MakeVisualizationFunction }[] + registerVisualizationFunction( + layerCtor: { new (layerManager: LayerManager): Layer }, + func: MakeVisualizationFunction ): void { - for (const { layer, func } of funcs) { - this._visualizationFunctions.set(layer.constructor.name, func); - } + this._visualizationFunctions.set(layerCtor.name, func); } - make(): FactoryProduct { - return this.makeRecursively(this._layerManager.getGroupDelegate()); + make(layerManager: LayerManager): FactoryProduct { + return this.makeRecursively(layerManager.getGroupDelegate()); } private makeRecursively(groupDelegate: GroupDelegate, numCollectedLayers: number = 0): FactoryProduct { @@ -191,6 +184,7 @@ export class LayerVisualizationFactory name: layer.getItemDelegate().getName(), data: layer.getLayerDelegate().getData(), colorScale, + settings: layer.getLayerDelegate().getSettingsContext().getDelegate().getValues(), }); } diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer.ts new file mode 100644 index 000000000..637b96b0e --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer.ts @@ -0,0 +1,26 @@ +import { WellborePick_api } from "@api"; +import { WellborePickLayerData, WellborePicksLayer } from "@modules/_shared/customDeckGlLayers/WellborePicksLayer"; + +import { VisualizationFunctionArgs } from "../VisualizationFactory"; + +export function makeWellborePicksLayer({ + id, + data, +}: VisualizationFunctionArgs): WellborePicksLayer { + const wellPicksData: WellborePickLayerData[] = data.map((wellborePick) => { + return { + easting: wellborePick.easting, + northing: wellborePick.northing, + wellBoreUwi: wellborePick.wellboreUuid, + tvdMsl: wellborePick.tvdMsl, + md: wellborePick.md, + slotName: "", + }; + }); + + return new WellborePicksLayer({ + id, + data: wellPicksData, + pickable: true, + }); +} diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellsLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellsLayer.ts new file mode 100644 index 000000000..1b33a0494 --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellsLayer.ts @@ -0,0 +1,82 @@ +import { WellboreTrajectory_api } from "@api"; + +import { Feature, FeatureCollection, GeoJsonProperties, GeometryCollection, LineString, Point } from "geojson"; + +import { AdvancedWellsLayer } from "../../../customDeckGlLayers/AdvancedWellsLayer"; +import { VisualizationFunctionArgs } from "../VisualizationFactory"; + +function wellTrajectoryToGeojson( + wellTrajectory: WellboreTrajectory_api +): Feature { + const point: Point = { + type: "Point", + coordinates: [wellTrajectory.eastingArr[0], wellTrajectory.northingArr[0], -wellTrajectory.tvdMslArr[0]], + }; + + const coordinates: LineString = { + type: "LineString", + coordinates: zipCoords(wellTrajectory.eastingArr, wellTrajectory.northingArr, wellTrajectory.tvdMslArr), + }; + + const color = [100, 100, 100]; + const lineWidth = 2; + const wellHeadSize = 1; + + const geometryCollection: Feature = { + type: "Feature", + geometry: { + type: "GeometryCollection", + geometries: [point, coordinates], + }, + properties: { + uuid: wellTrajectory.wellboreUuid, + name: wellTrajectory.uniqueWellboreIdentifier, + uwi: wellTrajectory.uniqueWellboreIdentifier, + color, + md: [wellTrajectory.mdArr], + lineWidth, + wellHeadSize, + }, + }; + + return geometryCollection; +} + +function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { + const coords: number[][] = []; + for (let i = 0; i < xArr.length; i++) { + coords.push([xArr[i], yArr[i], -zArr[i]]); + } + + return coords; +} + +export function makeWellsLayer({ + id, + data, + name, +}: VisualizationFunctionArgs): AdvancedWellsLayer { + // Filter out some wellbores that are known to be not working - this is a temporary solution + const filteredData = data.filter((wellbore) => wellbore.uniqueWellboreIdentifier !== "NO 34/4-K-3 AH"); + + // WellsLayer requires data in GeoJSON format in a FeatureCollection + const featureCollection: FeatureCollection = { + type: "FeatureCollection", + features: filteredData.map((wellTrajectory) => wellTrajectoryToGeojson(wellTrajectory)), + }; + + return new AdvancedWellsLayer({ + id, + data: featureCollection, + name, + refine: false, + wellNameVisible: true, + wellHeadStyle: { size: 1 }, + pickable: true, + ZIncreasingDownwards: false, + outline: false, + lineStyle: { + width: 2, + }, + }); +} diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/utils/colors.ts b/frontend/src/modules/_shared/LayerFramework/visualization/utils/colors.ts new file mode 100644 index 000000000..9d9022176 --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/visualization/utils/colors.ts @@ -0,0 +1,24 @@ +import { ColorScale } from "@lib/utils/ColorScale"; + +import { Rgb, parse } from "culori"; + +export function makeColorMapFunctionFromColorScale( + colorScale: ColorScale | undefined, + valueMin: number, + valueMax: number, + unnormalize = true +): ((value: number) => [number, number, number]) | undefined { + if (!colorScale) { + return undefined; + } + + return (value: number) => { + const nonNormalizedValue = unnormalize ? value * (valueMax - valueMin) + valueMin : value; + const interpolatedColor = colorScale.getColorForValue(nonNormalizedValue); + const color = parse(interpolatedColor) as Rgb; + if (color === undefined) { + return [0, 0, 0]; + } + return [color.r * 255, color.g * 255, color.b * 255]; + }; +} diff --git a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/AdvancedWellsLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/AdvancedWellsLayer.ts similarity index 97% rename from frontend/src/modules/3DViewerNew/view/customDeckGlLayers/AdvancedWellsLayer.ts rename to frontend/src/modules/_shared/customDeckGlLayers/AdvancedWellsLayer.ts index 112c3363b..117c42aee 100644 --- a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/AdvancedWellsLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/AdvancedWellsLayer.ts @@ -5,10 +5,6 @@ import { WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; export class AdvancedWellsLayer extends WellsLayer { static layerName: string = "WellsLayer"; - constructor(props: any) { - super(props); - } - filterSubLayer(context: FilterContext): boolean { if (context.layer.id.includes("names")) { return context.viewport.zoom > -2; diff --git a/frontend/src/modules/3DViewerNew/view/customDeckGlLayers/PlaceholderLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/PlaceholderLayer.ts similarity index 100% rename from frontend/src/modules/3DViewerNew/view/customDeckGlLayers/PlaceholderLayer.ts rename to frontend/src/modules/_shared/customDeckGlLayers/PlaceholderLayer.ts diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts new file mode 100644 index 000000000..810a891e2 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts @@ -0,0 +1,75 @@ +import { CompositeLayer, CompositeLayerProps, Layer, UpdateParameters } from "@deck.gl/core"; +import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; +import { Geometry } from "@luma.gl/engine"; + +export type SeismicFenceMeshLayerProps = { + data: { + vertices: Float32Array; + indices: Uint16Array; + properties: Float32Array; + }; + propertyRange: [number, number]; + colorMapFunction: (value: number) => [number, number, number]; +}; + +export class SeismicFenceMeshLayer extends CompositeLayer { + static layerName: string = "SeismicFenceMeshLayer"; + + // @ts-expect-error - private + state!: { + geometry: Geometry; + }; + + private makeColorsArray(): Float32Array { + const { data, propertyRange, colorMapFunction } = this.props; + const [minValue, maxValue] = propertyRange; + + const colors = new Float32Array(data.properties.length * 4); + + for (let i = 0; i < data.properties.length; i++) { + const [r, g, b] = colorMapFunction((data.properties[i] - minValue) / (maxValue - minValue)); + colors[i * 4 + 0] = r; + colors[i * 4 + 1] = g; + colors[i * 4 + 2] = b; + colors[i * 4 + 3] = 255; + } + + return colors; + } + + private makeMesh() { + const { data } = this.props; + + // Implementation of mesh creation + this.setState({ + geometry: new Geometry({ + attributes: { + positions: data.vertices, + colors: { + value: this.makeColorsArray(), + size: 4, + }, + }, + topology: "triangle-list", + indices: data.indices, + }), + }); + } + + updateState(params: UpdateParameters>>) { + super.updateState(params); + this.makeMesh(); + } + + renderLayers() { + const { geometry } = this.state; + + return new SimpleMeshLayer({ + id: "seismic-fence-mesh-layer", + data: [0], + mesh: geometry, + getPosition: [0, 0, 0], + getColor: [255, 255, 255], + }); + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts index 4c6ddd195..16329ae07 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts @@ -3,7 +3,7 @@ import { GeoJsonLayer, TextLayer } from "@deck.gl/layers"; import type { Feature, FeatureCollection } from "geojson"; -export type WellBorePickLayerData = { +export type WellborePickLayerData = { easting: number; northing: number; wellBoreUwi: string; @@ -19,7 +19,7 @@ type TextLayerData = { export type WellBorePicksLayerProps = { id: string; - data: WellBorePickLayerData[]; + data: WellborePickLayerData[]; }; export class WellborePicksLayer extends CompositeLayer { From c2764045b91c832bd35e7050952f8fc2af2a91b2 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 6 Feb 2025 13:31:58 +0100 Subject: [PATCH 34/97] wip --- .../makeRealizationSeismicCrosslineLayer.ts | 65 ++++++------------- .../SeismicFenceMeshLayer.ts | 23 +++---- 2 files changed, 33 insertions(+), 55 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts index 0f279b28e..7d525314b 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts @@ -9,23 +9,18 @@ function generatePointFenceMesh( numSamplesXY: number, minDepth: number, maxDepth: number, - numDepthSamples: number, - propertyArray: Float32Array + numDepthSamples: number ): { vertices: Float32Array; - indices: Uint16Array; - properties: Float32Array; - minPropValue: number; - maxPropValue: number; + indices: Uint32Array; } { const vertices = new Float32Array(numSamplesXY * numDepthSamples * 3); - const properties = new Float32Array(numSamplesXY * numDepthSamples); // Number of triangles to be drawn const numTriangles = (numSamplesXY - 1) * (numDepthSamples - 1) * 2; // Triangle strip indices - const indices = new Uint16Array(numTriangles * 3); + const indices = new Uint32Array(numTriangles * 3); const stepX = (endPoint[0] - startPoint[0]) / (numSamplesXY - 1); const stepY = (endPoint[1] - startPoint[1]) / (numSamplesXY - 1); @@ -33,11 +28,8 @@ function generatePointFenceMesh( let verticesIndex = 0; let indicesIndex = 0; - let propertiesIndex = 0; - let minPropValue = Infinity; - let maxPropValue = -Infinity; - for (let i = 0; i < numDepthSamples; i++) { + for (let col = 0; col < numSamplesXY; col++) { /* if (i > 1) { // Draw a degenerated triangle to move to the next row @@ -45,38 +37,24 @@ function generatePointFenceMesh( indices[indicesIndex++] = i * numSamplesXY; } */ - for (let j = 0; j < numSamplesXY; j++) { - vertices[verticesIndex++] = startPoint[0] + j * stepX; - vertices[verticesIndex++] = startPoint[1] + j * stepY; - vertices[verticesIndex++] = -(minDepth + i * stepZ); + for (let row = 0; row < numDepthSamples; row++) { + vertices[verticesIndex++] = col * stepX; + vertices[verticesIndex++] = col * stepY; + vertices[verticesIndex++] = -(row * stepZ); - const propValue = propertyArray[j * numDepthSamples + i]; - properties[propertiesIndex++] = propValue; - minPropValue = Math.min(minPropValue, propValue); - maxPropValue = Math.max(maxPropValue, propValue); + if (row > 0 && col > 0) { + indices[indicesIndex++] = (col - 1) * numDepthSamples + row - 1; + indices[indicesIndex++] = (col - 1) * numDepthSamples + row; + indices[indicesIndex++] = col * numDepthSamples + row - 1; - /* - if (i > 0) { - indices[indicesIndex++] = (i - 1) * numSamplesXY + j; - indices[indicesIndex++] = i * numSamplesXY + j; - } - */ - - if (i > 0 && j > 0) { - // Three indices for the triangle - indices[indicesIndex++] = (i - 1) * numSamplesXY + j; - indices[indicesIndex++] = i * numSamplesXY + j; - indices[indicesIndex++] = (i - 1) * numSamplesXY + j + 1; - - // Three indices for the triangle - indices[indicesIndex++] = i * numSamplesXY + j; - indices[indicesIndex++] = i * numSamplesXY + j + 1; - indices[indicesIndex++] = (i - 1) * numSamplesXY + j + 1; + indices[indicesIndex++] = col * numDepthSamples + row - 1; + indices[indicesIndex++] = (col - 1) * numDepthSamples + row; + indices[indicesIndex++] = col * numDepthSamples + row; } } } - return { vertices, indices, properties, minPropValue, maxPropValue }; + return { vertices, indices }; } export function makeRealizationSeismicCrosslineLayer({ @@ -90,16 +68,15 @@ export function makeRealizationSeismicCrosslineLayer({ const minDepth = data.z_min; const maxDepth = data.z_max; const numDepthSamples = data.z_samples; - const propertyArray = data.dataFloat32Arr; + const properties = data.dataFloat32Arr; - const { vertices, indices, properties, minPropValue, maxPropValue } = generatePointFenceMesh( + const { vertices, indices } = generatePointFenceMesh( startPoint, endPoint, numSamples, minDepth, maxDepth, - numDepthSamples, - propertyArray + numDepthSamples ); return new SeismicFenceMeshLayer({ @@ -109,7 +86,7 @@ export function makeRealizationSeismicCrosslineLayer({ indices, properties, }, - propertyRange: [minPropValue, maxPropValue], - colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, minPropValue, maxPropValue, false), + startPosition: [startPoint[0], startPoint[1], -minDepth], + colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), }); } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts index 810a891e2..14c388aef 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts @@ -3,12 +3,12 @@ import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; import { Geometry } from "@luma.gl/engine"; export type SeismicFenceMeshLayerProps = { + startPosition: [number, number, number]; data: { vertices: Float32Array; - indices: Uint16Array; + indices: Uint32Array; properties: Float32Array; }; - propertyRange: [number, number]; colorMapFunction: (value: number) => [number, number, number]; }; @@ -21,17 +21,16 @@ export class SeismicFenceMeshLayer extends CompositeLayer Date: Thu, 6 Feb 2025 16:10:36 +0100 Subject: [PATCH 35/97] wip --- .../primary/routers/seismic/converters.py | 189 ++---------------- .../primary/primary/routers/seismic/router.py | 62 +++--- .../primary/routers/seismic/schemas.py | 58 +----- frontend/src/api/autogen/types.gen.ts | 45 ++--- .../RealizationSeismicCrosslineLayer.ts | 18 +- .../RealizationSeismicDepthSliceLayer.ts | 36 ++-- .../RealizationSeismicInlineLayer.ts | 20 +- .../makeRealizationSeismicCrosslineLayer.ts | 92 --------- .../makeSeismicFenceMeshLayer.ts | 118 +++++++++++ .../settings/queries/queryDataTransforms.ts | 26 +-- .../view/components/LayersWrapper.tsx | 17 +- .../view/utils/seismicSliceUtils.ts | 160 --------------- .../visualization/VisualizationFactory.ts | 3 + .../SeismicFenceMeshLayer.ts | 18 +- 14 files changed, 257 insertions(+), 605 deletions(-) delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/view/utils/seismicSliceUtils.ts diff --git a/backend_py/primary/primary/routers/seismic/converters.py b/backend_py/primary/primary/routers/seismic/converters.py index 8819d4b41..56ab62f1c 100644 --- a/backend_py/primary/primary/routers/seismic/converters.py +++ b/backend_py/primary/primary/routers/seismic/converters.py @@ -1,190 +1,29 @@ -from typing import List -import math - -import orjson import numpy as np from numpy.typing import NDArray -import xtgeo - from webviz_pkg.core_utils.b64 import b64_encode_float_array_as_float32 + from primary.services.vds_access.response_types import VdsSliceMetadata from . import schemas -from ..surface import schemas as surface_schemas -from primary.services.utils.surface_to_float32 import surface_to_float32_numpy_array - -def surface_to_float32_array(values: np.ndarray) -> List[float]: - values = values.astype(np.float32) - np.ma.set_fill_value(values, np.nan) - values = np.ma.filled(values) - # Rotate 90 deg left. - # This will cause the width of to run along the X axis - # and height of along Y axis (starting from bottom.) - values = np.rot90(values) - return values.flatten().tolist() - - -def to_api_surface_data( - xtgeo_surf: xtgeo.RegularSurface, property_values: np.ndarray -) -> schemas.SurfaceMeshAndProperty: - """ - Create API SurfaceData from xtgeo regular surface - """ - float32_mesh = surface_to_float32_array(xtgeo_surf.values) - float32_property = surface_to_float32_array(property_values) - - return schemas.SurfaceMeshAndProperty( - x_ori=xtgeo_surf.xori, - y_ori=xtgeo_surf.yori, - x_count=xtgeo_surf.ncol, - y_count=xtgeo_surf.nrow, - x_inc=xtgeo_surf.xinc, - y_inc=xtgeo_surf.yinc, - x_min=xtgeo_surf.xmin, - x_max=xtgeo_surf.xmax, - y_min=xtgeo_surf.ymin, - y_max=xtgeo_surf.ymax, - mesh_value_min=xtgeo_surf.values.min(), - mesh_value_max=xtgeo_surf.values.max(), - property_value_min=property_values.min(), - property_value_max=property_values.max(), - rot_deg=xtgeo_surf.rotation, - mesh_data=orjson.dumps(float32_mesh).decode("utf-8"), # pylint: disable=maybe-no-member - property_data=orjson.dumps(float32_property).decode("utf-8"), # pylint: disable=maybe-no-member - ) - -def to_api_vds_inline_data( - flattened_slice_traces_array: NDArray[np.float32], metadata: VdsSliceMetadata) -> schemas.SeismicInlineData: - """ - Create API SeismicInlineData from VdsSliceMetadata and flattened slice traces array - """ - - return schemas.SeismicInlineData( - slice_traces_b64arr=b64_encode_float_array_as_float32(flattened_slice_traces_array), - start_utm_x=metadata.geospatial[0][0], - start_utm_y=metadata.geospatial[0][1], - end_utm_x=metadata.geospatial[1][0], - end_utm_y=metadata.geospatial[1][1], - crossline_min=metadata.y["min"], - crossline_max=metadata.y["max"], - crossline_no_samples=metadata.y["samples"], - z_min=metadata.x["min"], - z_max=metadata.x["max"], - z_samples=metadata.x["samples"], - z_unit=metadata.x["unit"], - value_min=np.nanmin(flattened_slice_traces_array), - value_max=np.nanmax(flattened_slice_traces_array), - - ) - -def to_api_vds_crossline_data( - flattened_slice_traces_array: NDArray[np.float32], metadata: VdsSliceMetadata) -> schemas.SeismicCrosslineData: +def to_api_vds_slice_data( + flattened_slice_traces_array: NDArray[np.float32], metadata: VdsSliceMetadata +) -> schemas.SeismicSliceData: """ Create API SeismicCrosslineData from VdsSliceMetadata and flattened slice traces array """ - return schemas.SeismicCrosslineData( + return schemas.SeismicSliceData( slice_traces_b64arr=b64_encode_float_array_as_float32(flattened_slice_traces_array), - start_utm_x=metadata.geospatial[0][0], - start_utm_y=metadata.geospatial[0][1], - end_utm_x=metadata.geospatial[1][0], - end_utm_y=metadata.geospatial[1][1], - inline_min=metadata.y["min"], - inline_max=metadata.y["max"], - inline_no_samples=metadata.y["samples"], - z_min=metadata.x["min"], - z_max=metadata.x["max"], - z_samples=metadata.x["samples"], - z_unit=metadata.x["unit"], + bbox_utm=metadata.geospatial, + u_min=metadata.x["min"], + u_max=metadata.x["max"], + u_num_samples=metadata.x["samples"], + u_unit=metadata.x["unit"], + v_min=metadata.y["min"], + v_max=metadata.y["max"], + v_num_samples=metadata.y["samples"], + v_unit=metadata.y["unit"], value_min=np.nanmin(flattened_slice_traces_array), value_max=np.nanmax(flattened_slice_traces_array), - - ) -def to_api_vds_depth_slice_data( - flattened_slice_traces_array: NDArray[np.float32], metadata: VdsSliceMetadata) -> surface_schemas.SurfaceDataFloat: - """ - Create API SeismicCrosslineData from VdsSliceMetadata and flattened slice traces array - """ - # VdsSliceMetadata(format=' schemas.SeismicInlineData: - """Get a seismic inline from a seismic cube. - """ + inline_no: int = Query(description="Inline number"), +) -> schemas.SeismicSliceData: + """Get a seismic inline from a seismic cube.""" seismic_access = await SeismicAccess.from_case_uuid_async( - authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name + authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name ) vds_handle: Optional[VdsHandle] = None @@ -70,12 +69,13 @@ async def get_inline_slice( vds_access = VdsAccess(sas_token=vds_handle.sas_token, vds_url=vds_handle.vds_url) - flattened_slice_traces_array,metadata = await vds_access.get_inline_slice( line_no=inline_no) - - return converters.to_api_vds_inline_data(flattened_slice_traces_array=flattened_slice_traces_array, - metadata=metadata + flattened_slice_traces_array, metadata = await vds_access.get_inline_slice(line_no=inline_no) + + return converters.to_api_vds_slice_data( + flattened_slice_traces_array=flattened_slice_traces_array, metadata=metadata ) - + + @router.get("/get_crossline_slice/") async def get_crossline_slice( authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), @@ -85,12 +85,11 @@ async def get_crossline_slice( seismic_attribute: str = Query(description="Seismic cube attribute"), time_or_interval_str: str = Query(description="Timestamp or timestep"), observed: bool = Query(description="Observed or simulated"), - crossline_no:int = Query(description="Crossline number") -) -> schemas.SeismicCrosslineData: - """Get a seismic crossline from a seismic cube. - """ + crossline_no: int = Query(description="Crossline number"), +) -> schemas.SeismicSliceData: + """Get a seismic crossline from a seismic cube.""" seismic_access = await SeismicAccess.from_case_uuid_async( - authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name + authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name ) vds_handle: Optional[VdsHandle] = None @@ -109,11 +108,13 @@ async def get_crossline_slice( vds_access = VdsAccess(sas_token=vds_handle.sas_token, vds_url=vds_handle.vds_url) - flattened_slice_traces_array,metadata = await vds_access.get_crossline_slice( line_no=crossline_no) + flattened_slice_traces_array, metadata = await vds_access.get_crossline_slice(line_no=crossline_no) - return converters.to_api_vds_crossline_data(flattened_slice_traces_array=flattened_slice_traces_array, - metadata=metadata + return converters.to_api_vds_slice_data( + flattened_slice_traces_array=flattened_slice_traces_array, metadata=metadata ) + + @router.get("/get_depth_slice/") async def get_depth_slice( authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), @@ -123,12 +124,11 @@ async def get_depth_slice( seismic_attribute: str = Query(description="Seismic cube attribute"), time_or_interval_str: str = Query(description="Timestamp or timestep"), observed: bool = Query(description="Observed or simulated"), - depth:int = Query(description="depth") -) -> surface_schemas.SurfaceDataFloat: - """Get a seismic depth slice from a seismic cube. - """ + depth: int = Query(description="depth"), +) -> schemas.SeismicSliceData: + """Get a seismic depth slice from a seismic cube.""" seismic_access = await SeismicAccess.from_case_uuid_async( - authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name + authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name ) vds_handle: Optional[VdsHandle] = None @@ -147,11 +147,13 @@ async def get_depth_slice( vds_access = VdsAccess(sas_token=vds_handle.sas_token, vds_url=vds_handle.vds_url) - flattened_slice_traces_array,metadata = await vds_access.get_depth_slice( depth=depth) - - return converters.to_api_vds_depth_slice_data(flattened_slice_traces_array=flattened_slice_traces_array, - metadata=metadata + flattened_slice_traces_array, metadata = await vds_access.get_depth_slice(depth=depth) + + return converters.to_api_vds_slice_data( + flattened_slice_traces_array=flattened_slice_traces_array, metadata=metadata ) + + @router.post("/get_seismic_fence/") async def post_get_seismic_fence( authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), @@ -202,7 +204,7 @@ async def post_get_seismic_fence( coordinate_system=VdsCoordinateSystem.CDP, ) meta: VdsMetadata = await vds_access.get_metadata_async() - + if len(meta.axis) != 3: raise ValueError(f"Expected 3 axes, got {len(meta.axis)}") depth_axis_meta = meta.axis[2] diff --git a/backend_py/primary/primary/routers/seismic/schemas.py b/backend_py/primary/primary/routers/seismic/schemas.py index 20797f00e..9f2841084 100644 --- a/backend_py/primary/primary/routers/seismic/schemas.py +++ b/backend_py/primary/primary/routers/seismic/schemas.py @@ -20,6 +20,7 @@ class SeismicCubeMeta(BaseModel): z_max: float z_inc: float + class SeismicFencePolyline(BaseModel): """ (x, y) points defining a polyline in domain coordinate system, to retrieve fence of seismic data. @@ -69,53 +70,16 @@ class SeismicFenceData(BaseModel): max_fence_depth: float -class SeismicInlineData(BaseModel): +class SeismicSliceData(BaseModel): slice_traces_b64arr: B64FloatArray - start_utm_x: float - start_utm_y: float - end_utm_x: float - end_utm_y: float - crossline_min: int - crossline_max: int - crossline_no_samples: int - z_min: float - z_max: float - z_samples: int - z_unit: str + bbox_utm: List[List[float]] + u_min: int + u_max: int + u_num_samples: int + u_unit: str + v_min: float + v_max: float + v_num_samples: int + v_unit: str value_min: float value_max: float - -class SeismicCrosslineData(BaseModel): - slice_traces_b64arr: B64FloatArray - start_utm_x: float - start_utm_y: float - end_utm_x: float - end_utm_y: float - inline_min: int - inline_max: int - inline_no_samples: int - z_min: float - z_max: float - z_samples: int - z_unit: str - value_min: float - value_max: float - -class SurfaceMeshAndProperty(BaseModel): - x_ori: float - y_ori: float - x_count: int - y_count: int - x_inc: float - y_inc: float - x_min: float - x_max: float - y_min: float - y_max: float - mesh_value_min: float - mesh_value_max: float - property_value_min: float - property_value_max: float - rot_deg: float - mesh_data: str - property_data: str diff --git a/frontend/src/api/autogen/types.gen.ts b/frontend/src/api/autogen/types.gen.ts index c65286159..aa4822f00 100644 --- a/frontend/src/api/autogen/types.gen.ts +++ b/frontend/src/api/autogen/types.gen.ts @@ -544,23 +544,6 @@ export type RftWellInfo_api = { timestamps_utc_ms: Array; }; -export type SeismicCrosslineData_api = { - slice_traces_b64arr: B64FloatArray_api; - start_utm_x: number; - start_utm_y: number; - end_utm_x: number; - end_utm_y: number; - inline_min: number; - inline_max: number; - inline_no_samples: number; - z_min: number; - z_max: number; - z_samples: number; - z_unit: string; - value_min: number; - value_max: number; -}; - export type SeismicCubeMeta_api = { seismic_attribute: string; iso_date_or_interval: string; @@ -626,19 +609,17 @@ export type SeismicFencePolyline_api = { y_points: Array; }; -export type SeismicInlineData_api = { +export type SeismicSliceData_api = { slice_traces_b64arr: B64FloatArray_api; - start_utm_x: number; - start_utm_y: number; - end_utm_x: number; - end_utm_y: number; - crossline_min: number; - crossline_max: number; - crossline_no_samples: number; - z_min: number; - z_max: number; - z_samples: number; - z_unit: string; + bbox_utm: Array>; + u_min: number; + u_max: number; + u_num_samples: number; + u_unit: string; + v_min: number; + v_max: number; + v_num_samples: number; + v_unit: string; value_min: number; value_max: number; }; @@ -3164,7 +3145,7 @@ export type GetInlineSliceResponses_api = { /** * Successful Response */ - 200: SeismicInlineData_api; + 200: SeismicSliceData_api; }; export type GetInlineSliceResponse_api = GetInlineSliceResponses_api[keyof GetInlineSliceResponses_api]; @@ -3218,7 +3199,7 @@ export type GetCrosslineSliceResponses_api = { /** * Successful Response */ - 200: SeismicCrosslineData_api; + 200: SeismicSliceData_api; }; export type GetCrosslineSliceResponse_api = GetCrosslineSliceResponses_api[keyof GetCrosslineSliceResponses_api]; @@ -3272,7 +3253,7 @@ export type GetDepthSliceResponses_api = { /** * Successful Response */ - 200: SurfaceDataFloat_api; + 200: SeismicSliceData_api; }; export type GetDepthSliceResponse_api = GetDepthSliceResponses_api[keyof GetDepthSliceResponses_api]; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index f40543658..10f7b6764 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -12,12 +12,12 @@ import { isEqual } from "lodash"; import { RealizationSeismicCrosslineSettingsContext } from "./RealizationSeismicCrosslineSettingsContext"; import { RealizationSeismicCrosslineSettings } from "./types"; -import { SeismicCrosslineData_trans, transformSeismicCrossline } from "../../../settings/queries/queryDataTransforms"; +import { SeismicSliceData_trans, transformSeismicSlice } from "../../../settings/queries/queryDataTransforms"; export class RealizationSeismicCrosslineLayer - implements Layer + implements Layer { - private _layerDelegate: LayerDelegate; + private _layerDelegate: LayerDelegate; private _itemDelegate: ItemDelegate; constructor(layerManager: LayerManager) { @@ -38,7 +38,7 @@ export class RealizationSeismicCrosslineLayer return this._itemDelegate; } - getLayerDelegate(): LayerDelegate { + getLayerDelegate(): LayerDelegate { return this._layerDelegate; } @@ -56,9 +56,9 @@ export class RealizationSeismicCrosslineLayer } return { - x: [data.start_utm_x, data.end_utm_x], - y: [data.start_utm_y, data.end_utm_y], - z: [data.z_min, data.z_max], + x: [data.bbox_utm[0][0], data.bbox_utm[1][0]], + y: [data.bbox_utm[0][1], data.bbox_utm[1][1]], + z: [data.u_min, data.u_max], }; } @@ -71,7 +71,7 @@ export class RealizationSeismicCrosslineLayer return [data.value_min, data.value_max]; } - fetchData(queryClient: QueryClient): Promise { + fetchData(queryClient: QueryClient): Promise { const settings = this.getSettingsContext().getDelegate().getSettings(); const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); @@ -104,7 +104,7 @@ export class RealizationSeismicCrosslineLayer }, }), }) - .then((data) => transformSeismicCrossline(data)); + .then((data) => transformSeismicSlice(data)); return seismicSlicePromise; } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts index c869292fb..221ce6cbe 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts @@ -1,11 +1,10 @@ -import { SurfaceDataPng_api, getCrosslineSliceOptions, getDepthSliceOptions } from "@api"; +import { getDepthSliceOptions } from "@api"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { SurfaceDataFloat_trans, transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; import { QueryClient } from "@tanstack/react-query"; import { isEqual } from "lodash"; @@ -13,15 +12,12 @@ import { isEqual } from "lodash"; import { RealizationSeismicDepthSliceSettingsContext } from "./RealizationSeismicDepthSliceSettingsContext"; import { RealizationSeismicDepthSliceSettings } from "./types"; -import { SeismicCrosslineData_trans, transformSeismicCrossline } from "../../../settings/queries/queryDataTransforms"; +import { SeismicSliceData_trans, transformSeismicSlice } from "../../../settings/queries/queryDataTransforms"; export class RealizationSeismicDepthSliceLayer - implements Layer + implements Layer { - private _layerDelegate: LayerDelegate< - RealizationSeismicDepthSliceSettings, - SurfaceDataFloat_trans | SurfaceDataPng_api - >; + private _layerDelegate: LayerDelegate; private _itemDelegate: ItemDelegate; constructor(layerManager: LayerManager) { @@ -42,10 +38,7 @@ export class RealizationSeismicDepthSliceLayer return this._itemDelegate; } - getLayerDelegate(): LayerDelegate< - RealizationSeismicDepthSliceSettings, - SurfaceDataFloat_trans | SurfaceDataPng_api - > { + getLayerDelegate(): LayerDelegate { return this._layerDelegate; } makeBoundingBox(): BoundingBox | null { @@ -54,10 +47,17 @@ export class RealizationSeismicDepthSliceLayer return null; } + const settings = this.getSettingsContext().getDelegate().getSettings(); + const seismicDepth = settings[SettingType.SEISMIC_DEPTH_SLICE].getDelegate().getValue(); + + if (seismicDepth === null) { + return null; + } + return { - x: [data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.max_x], - y: [data.transformed_bbox_utm.min_y, data.transformed_bbox_utm.max_y], - z: [data.value_min, data.value_max], + x: [data.bbox_utm[0][0], data.bbox_utm[1][0]], + y: [data.bbox_utm[0][1], data.bbox_utm[1][1]], + z: [seismicDepth, seismicDepth], }; } doSettingsChangesRequireDataRefetch( @@ -76,13 +76,13 @@ export class RealizationSeismicDepthSliceLayer return [data.value_min, data.value_max]; } - fetchData(queryClient: QueryClient): Promise { + fetchData(queryClient: QueryClient): Promise { const settings = this.getSettingsContext().getDelegate().getSettings(); const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); const seismicDepth = settings[SettingType.SEISMIC_DEPTH_SLICE].getDelegate().getValue(); const queryKey = [ @@ -109,7 +109,7 @@ export class RealizationSeismicDepthSliceLayer }, }), }) - .then((data) => transformSurfaceData(data)); + .then((data) => transformSeismicSlice(data)); return seismicSlicePromise; } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts index 5c0af36d7..1cb6c3539 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts @@ -12,10 +12,10 @@ import { isEqual } from "lodash"; import { RealizationSeismicInlineSettingsContext } from "./RealizationSeismicInlineSettingsContext"; import { RealizationSeismicInlineSettings } from "./types"; -import { SeismicInlineData_trans, transformSeismicInline } from "../../../settings/queries/queryDataTransforms"; +import { SeismicSliceData_trans, transformSeismicSlice } from "../../../settings/queries/queryDataTransforms"; -export class RealizationSeismicInlineLayer implements Layer { - private _layerDelegate: LayerDelegate; +export class RealizationSeismicInlineLayer implements Layer { + private _layerDelegate: LayerDelegate; private _itemDelegate: ItemDelegate; constructor(layerManager: LayerManager) { @@ -36,7 +36,7 @@ export class RealizationSeismicInlineLayer implements Layer { + getLayerDelegate(): LayerDelegate { return this._layerDelegate; } @@ -54,9 +54,9 @@ export class RealizationSeismicInlineLayer implements Layer { + fetchData(queryClient: QueryClient): Promise { const settings = this.getSettingsContext().getDelegate().getSettings(); const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); const seismicInlineNumber = settings[SettingType.SEISMIC_INLINE].getDelegate().getValue(); const queryKey = [ @@ -102,7 +102,7 @@ export class RealizationSeismicInlineLayer implements Layer transformSeismicInline(data)); + .then((data) => transformSeismicSlice(data)); return seismicSlicePromise; } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts deleted file mode 100644 index 7d525314b..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { SeismicCrosslineData_trans } from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; -import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; -import { SeismicFenceMeshLayer } from "@modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer"; - -function generatePointFenceMesh( - startPoint: number[], - endPoint: number[], - numSamplesXY: number, - minDepth: number, - maxDepth: number, - numDepthSamples: number -): { - vertices: Float32Array; - indices: Uint32Array; -} { - const vertices = new Float32Array(numSamplesXY * numDepthSamples * 3); - - // Number of triangles to be drawn - const numTriangles = (numSamplesXY - 1) * (numDepthSamples - 1) * 2; - - // Triangle strip indices - const indices = new Uint32Array(numTriangles * 3); - - const stepX = (endPoint[0] - startPoint[0]) / (numSamplesXY - 1); - const stepY = (endPoint[1] - startPoint[1]) / (numSamplesXY - 1); - const stepZ = (maxDepth - minDepth) / (numDepthSamples - 1); - - let verticesIndex = 0; - let indicesIndex = 0; - - for (let col = 0; col < numSamplesXY; col++) { - /* - if (i > 1) { - // Draw a degenerated triangle to move to the next row - indices[indicesIndex++] = (i - 1) * numSamplesXY + numSamplesXY - 1; - indices[indicesIndex++] = i * numSamplesXY; - } - */ - for (let row = 0; row < numDepthSamples; row++) { - vertices[verticesIndex++] = col * stepX; - vertices[verticesIndex++] = col * stepY; - vertices[verticesIndex++] = -(row * stepZ); - - if (row > 0 && col > 0) { - indices[indicesIndex++] = (col - 1) * numDepthSamples + row - 1; - indices[indicesIndex++] = (col - 1) * numDepthSamples + row; - indices[indicesIndex++] = col * numDepthSamples + row - 1; - - indices[indicesIndex++] = col * numDepthSamples + row - 1; - indices[indicesIndex++] = (col - 1) * numDepthSamples + row; - indices[indicesIndex++] = col * numDepthSamples + row; - } - } - } - - return { vertices, indices }; -} - -export function makeRealizationSeismicCrosslineLayer({ - id, - data, - colorScale, -}: VisualizationFunctionArgs): SeismicFenceMeshLayer { - const startPoint = [data.start_utm_x, data.start_utm_y]; - const endPoint = [data.end_utm_x, data.end_utm_y]; - const numSamples = data.inline_no_samples; - const minDepth = data.z_min; - const maxDepth = data.z_max; - const numDepthSamples = data.z_samples; - const properties = data.dataFloat32Arr; - - const { vertices, indices } = generatePointFenceMesh( - startPoint, - endPoint, - numSamples, - minDepth, - maxDepth, - numDepthSamples - ); - - return new SeismicFenceMeshLayer({ - id, - data: { - vertices, - indices, - properties, - }, - startPosition: [startPoint[0], startPoint[1], -minDepth], - colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), - }); -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts new file mode 100644 index 000000000..2c1e61adf --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -0,0 +1,118 @@ +import { SeismicSliceData_trans } from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; +import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; +import { SeismicFenceMeshLayer } from "@modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer"; + +/* + * Generate a mesh for a seismic fence plot + * @param numSamplesU The number of samples in the U direction, which is the first axis and often in depth direction + * @param numSamplesV The number of samples in the V direction, which is the second axis and often in inline/crossline direction + * @param transformUVToXYZ A function that takes the U and V coordinates (∈ [0,1]) and returns the X, Y, and Z coordinates + * + * @returns The vertices and indices of the mesh + */ +function generatePointFenceMesh( + numSamplesU: number, + numSamplesV: number, + transformUVToXYZ: (u: number, v: number) => [number, number, number] +): { + vertices: Float32Array; + indices: Uint32Array; +} { + const vertices = new Float32Array(numSamplesU * numSamplesV * 3); + + // Number of triangles to be drawn + const numTriangles = (numSamplesU - 1) * (numSamplesV - 1) * 2; + + // Triangle strip indices + const indices = new Uint32Array(numTriangles * 3); + + const stepU = 1.0 / (numSamplesU - 1); + const stepV = 1.0 / (numSamplesV - 1); + + let verticesIndex = 0; + let indicesIndex = 0; + + for (let v = 0; v < numSamplesV; v++) { + /* + if (i > 1) { + // Draw a degenerated triangle to move to the next row + indices[indicesIndex++] = (i - 1) * numSamplesXY + numSamplesXY - 1; + indices[indicesIndex++] = i * numSamplesXY; + } + */ + for (let u = 0; u < numSamplesU; u++) { + const [x, y, z] = transformUVToXYZ(u * stepU, v * stepV); + vertices[verticesIndex++] = x; + vertices[verticesIndex++] = y; + vertices[verticesIndex++] = z; + + if (u > 0 && v > 0) { + indices[indicesIndex++] = (v - 1) * numSamplesU + u - 1; + indices[indicesIndex++] = (v - 1) * numSamplesU + u; + indices[indicesIndex++] = v * numSamplesU + u - 1; + + indices[indicesIndex++] = v * numSamplesU + u - 1; + indices[indicesIndex++] = (v - 1) * numSamplesU + u; + indices[indicesIndex++] = v * numSamplesU + u; + } + } + } + + return { vertices, indices }; +} + +export enum Plane { + CROSSLINE = "CROSSLINE", + INLINE = "INLINE", + DEPTH = "DEPTH", +} + +export function makeSeismicFenceMeshLayerFunction(plane: Plane) { + return function makeSeismicFenceMeshLayer({ + id, + data, + colorScale, + settings, + }: VisualizationFunctionArgs): SeismicFenceMeshLayer { + const bbox = data.bbox_utm; + const properties = data.dataFloat32Arr; + + let startPosition: [number, number, number] = [0, 0, 0]; + + let transformUVToXYZ: (u: number, v: number) => [number, number, number] = () => { + throw new Error("transformUVToXYZ not implemented"); + }; + + if (plane === Plane.CROSSLINE || plane === Plane.INLINE) { + startPosition = [bbox[0][0], bbox[0][1], -data.u_min]; + transformUVToXYZ = (u: number, v: number): [number, number, number] => { + const x = v * (bbox[1][0] - bbox[0][0]); + const y = v * (bbox[1][1] - bbox[0][1]); + const z = -(u * (data.u_max - data.u_min)); + return [x, y, z]; + }; + } else if (plane === Plane.DEPTH) { + startPosition = [bbox[0][0], bbox[0][1], -settings.seismicDepthSlice]; + transformUVToXYZ = (u: number, v: number): [number, number, number] => { + const x = u * (bbox[1][0] - bbox[0][0]) + v * (bbox[3][0] - bbox[0][0]); // Diagonal across bounding box + const y = u * (bbox[1][1] - bbox[0][1]) + v * (bbox[3][1] - bbox[0][1]); // Diagonal across bounding box + const z = 0; + return [x, y, z]; + }; + } + + const { vertices, indices } = generatePointFenceMesh(data.u_num_samples, data.v_num_samples, transformUVToXYZ); + + return new SeismicFenceMeshLayer({ + id, + data: { + vertices, + indices, + properties, + }, + startPosition, + colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), + }); + }; +} diff --git a/frontend/src/modules/3DViewerNew/settings/queries/queryDataTransforms.ts b/frontend/src/modules/3DViewerNew/settings/queries/queryDataTransforms.ts index 430fa4555..6dc734f2a 100644 --- a/frontend/src/modules/3DViewerNew/settings/queries/queryDataTransforms.ts +++ b/frontend/src/modules/3DViewerNew/settings/queries/queryDataTransforms.ts @@ -1,35 +1,17 @@ -import { SeismicCrosslineData_api, SeismicInlineData_api } from "@api"; +import { SeismicSliceData_api } from "@api"; import { b64DecodeFloatArrayToFloat32 } from "@modules/_shared/base64"; -export type SeismicInlineData_trans = Omit & { +export type SeismicSliceData_trans = Omit & { dataFloat32Arr: Float32Array; }; -export function transformSeismicInline(apiData: SeismicInlineData_api): SeismicInlineData_trans { +export function transformSeismicSlice(apiData: SeismicSliceData_api): SeismicSliceData_trans { const startTS = performance.now(); const { slice_traces_b64arr, ...untransformedData } = apiData; const dataFloat32Arr = b64DecodeFloatArrayToFloat32(slice_traces_b64arr); - console.debug(`transformSeismicInline() took: ${(performance.now() - startTS).toFixed(1)}ms`); - - return { - ...untransformedData, - dataFloat32Arr: dataFloat32Arr, - }; -} - -export type SeismicCrosslineData_trans = Omit & { - dataFloat32Arr: Float32Array; -}; - -export function transformSeismicCrossline(apiData: SeismicCrosslineData_api): SeismicCrosslineData_trans { - const startTS = performance.now(); - - const { slice_traces_b64arr, ...untransformedData } = apiData; - const dataFloat32Arr = b64DecodeFloatArrayToFloat32(slice_traces_b64arr); - - console.debug(`transformSeismicCrossline() took: ${(performance.now() - startTS).toFixed(1)}ms`); + console.debug(`transformSeismicSlice() took: ${(performance.now() - startTS).toFixed(1)}ms`); return { ...untransformedData, diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 8c6b1795f..69093d26e 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -13,9 +13,14 @@ import { PreferredViewLayout } from "@modules/2DViewer/types"; import { IntersectionRealizationGridLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer"; import { RealizationGridLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer"; import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; +import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer"; +import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; import { makeGrid3DLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer"; import { makeIntersectionLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer"; -import { makeRealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeRealizationSeismicCrosslineLayer"; +import { + Plane, + makeSeismicFenceMeshLayerFunction, +} from "@modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer"; import { LayerManager, LayerManagerTopic } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; import { BoundingBox } from "@modules/_shared/LayerFramework/interfaces"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; @@ -45,7 +50,15 @@ VISUALIZATION_FACTORY.registerVisualizationFunction(RealizationGridLayer, makeGr VISUALIZATION_FACTORY.registerVisualizationFunction(IntersectionRealizationGridLayer, makeIntersectionLayer); VISUALIZATION_FACTORY.registerVisualizationFunction( RealizationSeismicCrosslineLayer, - makeRealizationSeismicCrosslineLayer + makeSeismicFenceMeshLayerFunction(Plane.CROSSLINE) +); +VISUALIZATION_FACTORY.registerVisualizationFunction( + RealizationSeismicInlineLayer, + makeSeismicFenceMeshLayerFunction(Plane.INLINE) +); +VISUALIZATION_FACTORY.registerVisualizationFunction( + RealizationSeismicDepthSliceLayer, + makeSeismicFenceMeshLayerFunction(Plane.DEPTH) ); export type LayersWrapperProps = { diff --git a/frontend/src/modules/3DViewerNew/view/utils/seismicSliceUtils.ts b/frontend/src/modules/3DViewerNew/view/utils/seismicSliceUtils.ts deleted file mode 100644 index 78e3763cc..000000000 --- a/frontend/src/modules/3DViewerNew/view/utils/seismicSliceUtils.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { - SeismicCrosslineData_trans, - SeismicInlineData_trans, -} from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; - -export type SeismicInlineLayerData = { - pointsFloat32Arr: Float32Array; - polysUint32Arr: Uint32Array; - propertyFloat32Arr: Float32Array; - minValue: number; - maxValue: number; -}; -export type SeismicCrosslineLayerData = { - pointsFloat32Arr: Float32Array; - polysUint32Arr: Uint32Array; - propertyFloat32Arr: Float32Array; - minValue: number; - maxValue: number; -}; -type Point = { x: number; y: number }; - -function interpolatePoints(start: Point, end: Point, numSamples: number): Point[] { - const result: Point[] = []; - - const intervalX = (end.x - start.x) / (numSamples - 1); - - for (let i = 0; i < numSamples; i++) { - const x = start.x + i * intervalX; - const y = start.y + ((x - start.x) / (end.x - start.x)) * (end.y - start.y); - result.push({ x, y }); - } - - return result; -} -export function createSeismicInlineLayerData(seismicInlineData: SeismicInlineData_trans): SeismicInlineLayerData { - const origPoint: Point = { x: seismicInlineData.start_utm_x, y: seismicInlineData.start_utm_y }; - const endPoint: Point = { x: seismicInlineData.end_utm_x, y: seismicInlineData.end_utm_y }; - const numSamples = seismicInlineData.crossline_no_samples; - const interpolatedPointsXY = interpolatePoints(origPoint, endPoint, numSamples); - - const minDepth = seismicInlineData.z_min; - const maxDepth = seismicInlineData.z_max; - const depthSamples = seismicInlineData.z_samples; - - const cellCountU = interpolatedPointsXY.length - 1; - const cellCountV = depthSamples - 1; - - const depthIncrement = (maxDepth - minDepth) / depthSamples; - const pointsFloat32Arr = new Float32Array((cellCountU + 1) * (cellCountV + 1) * 3); - const polysUint32Arr = new Uint32Array(cellCountU * cellCountV * 5); // 5 = 4 vertices + polygon count - - let pointIndex = 0; - let polygonIndex = 0; - - for (let j = 0; j <= cellCountV; j++) { - for (let i = 0; i <= cellCountU; i++) { - pointsFloat32Arr[pointIndex++] = interpolatedPointsXY[i].x; - pointsFloat32Arr[pointIndex++] = interpolatedPointsXY[i].y; - pointsFloat32Arr[pointIndex++] = minDepth + j * depthIncrement; - - if (i < cellCountU && j < cellCountV) { - polysUint32Arr[polygonIndex++] = 4; - polysUint32Arr[polygonIndex++] = i + j * (cellCountU + 1); - polysUint32Arr[polygonIndex++] = i + 1 + j * (cellCountU + 1); - polysUint32Arr[polygonIndex++] = i + 1 + (j + 1) * (cellCountU + 1); - polysUint32Arr[polygonIndex++] = i + (j + 1) * (cellCountU + 1); - } - } - } - const propertyArr = seismicInlineData.dataFloat32Arr; - const transposedPropertyArr = []; - for (let j = 0; j < depthSamples; j++) { - for (let i = 0; i < cellCountU; i++) { - transposedPropertyArr.push(propertyArr[i * depthSamples + j]); - } - } - const chunkSize = 10000; - let minValue = Infinity; - let maxValue = -Infinity; - - for (let i = 0; i < transposedPropertyArr.length; i += chunkSize) { - const chunk = transposedPropertyArr.slice(i, i + chunkSize); - const chunkMin = Math.min(...chunk); - const chunkMax = Math.max(...chunk); - - if (chunkMin < minValue) minValue = chunkMin; - if (chunkMax > maxValue) maxValue = chunkMax; - } - return { - pointsFloat32Arr, - polysUint32Arr, - propertyFloat32Arr: new Float32Array(transposedPropertyArr), - minValue, - maxValue, - }; -} -export function createSeismicCrosslineLayerData( - seismicCrosslineData: SeismicCrosslineData_trans -): SeismicCrosslineLayerData { - const origPoint: Point = { x: seismicCrosslineData.start_utm_x, y: seismicCrosslineData.start_utm_y }; - const endPoint: Point = { x: seismicCrosslineData.end_utm_x, y: seismicCrosslineData.end_utm_y }; - const numSamples = seismicCrosslineData.inline_no_samples; - const interpolatedPointsXY = interpolatePoints(origPoint, endPoint, numSamples); - - const minDepth = seismicCrosslineData.z_min; - const maxDepth = seismicCrosslineData.z_max; - const depthSamples = seismicCrosslineData.z_samples; - - const cellCountU = interpolatedPointsXY.length - 1; - const cellCountV = depthSamples - 1; - - const depthIncrement = (maxDepth - minDepth) / depthSamples; - const pointsFloat32Arr = new Float32Array((cellCountU + 1) * (cellCountV + 1) * 3); - const polysUint32Arr = new Uint32Array(cellCountU * cellCountV * 5); // 5 = 4 vertices + polygon count - - let pointIndex = 0; - let polygonIndex = 0; - - for (let j = 0; j <= cellCountV; j++) { - for (let i = 0; i <= cellCountU; i++) { - pointsFloat32Arr[pointIndex++] = interpolatedPointsXY[i].x; - pointsFloat32Arr[pointIndex++] = interpolatedPointsXY[i].y; - pointsFloat32Arr[pointIndex++] = minDepth + j * depthIncrement; - - if (i < cellCountU && j < cellCountV) { - polysUint32Arr[polygonIndex++] = 4; - polysUint32Arr[polygonIndex++] = i + j * (cellCountU + 1); - polysUint32Arr[polygonIndex++] = i + 1 + j * (cellCountU + 1); - polysUint32Arr[polygonIndex++] = i + 1 + (j + 1) * (cellCountU + 1); - polysUint32Arr[polygonIndex++] = i + (j + 1) * (cellCountU + 1); - } - } - } - const propertyArr = seismicCrosslineData.dataFloat32Arr; - const transposedPropertyArr = []; - for (let j = 0; j < depthSamples; j++) { - for (let i = 0; i < cellCountU; i++) { - transposedPropertyArr.push(propertyArr[i * depthSamples + j]); - } - } - const chunkSize = 10000; - let minValue = Infinity; - let maxValue = -Infinity; - - for (let i = 0; i < transposedPropertyArr.length; i += chunkSize) { - const chunk = transposedPropertyArr.slice(i, i + chunkSize); - const chunkMin = Math.min(...chunk); - const chunkMax = Math.max(...chunk); - - if (chunkMin < minValue) minValue = chunkMin; - if (chunkMax > maxValue) maxValue = chunkMax; - } - return { - pointsFloat32Arr, - polysUint32Arr, - propertyFloat32Arr: new Float32Array(transposedPropertyArr), - minValue, - maxValue, - }; -} diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts index 22075410c..90b1094a5 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts @@ -66,6 +66,9 @@ export class VisualizationFactory { layerCtor: { new (layerManager: LayerManager): Layer }, func: MakeVisualizationFunction ): void { + if (this._visualizationFunctions.has(layerCtor.name)) { + throw new Error(`Visualization function for layer ${layerCtor.name} already registered`); + } this._visualizationFunctions.set(layerCtor.name, func); } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts index 14c388aef..9238112ae 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts @@ -64,13 +64,15 @@ export class SeismicFenceMeshLayer extends CompositeLayer Date: Thu, 6 Feb 2025 18:04:59 +0100 Subject: [PATCH 36/97] wip --- .../view/components/LayersWrapper.tsx | 1 + .../view/components/ReadoutWrapper.tsx | 6 +- .../customDeckGlLayers/DragHandleLayer.ts | 165 ++++++++++++++++++ 3 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 69093d26e..cccc82996 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -123,6 +123,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { "editable-polylines-layer", "polylines-layer", "hover-point-layer", + "est", ], }); viewerLayers.push(...view.layers); diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index ac2b70f35..1e2841587 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -7,6 +7,7 @@ import { WorkbenchSession } from "@framework/WorkbenchSession"; import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { IntersectionPolylinesEvent } from "@framework/userCreatedItems/IntersectionPolylines"; import { SubsurfaceViewerWithCameraState } from "@modules/_shared/components/SubsurfaceViewerWithCameraState"; +import { DragDirection, DragHandleLayer } from "@modules/_shared/customDeckGlLayers/DragHandleLayer"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { BoundingBox3D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; @@ -142,7 +143,10 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { setLayerPickingInfo(pickingInfo); } - let adjustedLayers = [...props.layers]; + let adjustedLayers = [ + ...props.layers, + new DragHandleLayer({ id: "est", position: [456000, 6818471, -500], dragDirection: DragDirection.X }), + ]; if (!gridVisible) { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts new file mode 100644 index 000000000..81e99082a --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts @@ -0,0 +1,165 @@ +import { CompositeLayer, PickingInfo } from "@deck.gl/core"; +import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; +import { Geometry } from "@luma.gl/engine"; + +export enum DragDirection { + X = "X", + Y = "Y", + Z = "Z", + XY = "XY", + XZ = "XZ", + YZ = "YZ", + XYZ = "XYZ", +} + +export type DragHandleLayerProps = { + dragDirection: DragDirection; + position: [number, number, number]; +}; + +function makeArrowMesh(): Geometry { + const numCircleVertices = 20; + const vertices = new Float32Array((numCircleVertices + 1) * 4 * 3); // 4x circle with midpoint + const indices = new Uint32Array(numCircleVertices * 3 * 3 + numCircleVertices * 2 * 3); + let triangleIndex = 0; + let vertexIndex = 0; + const coneRadius = 2; + const shaftRadius = 1; + const coneHeight = 3; + const shaftHeight = 3; + + // The tip of the cone + vertices[vertexIndex++] = 0; + vertices[vertexIndex++] = 0; + vertices[vertexIndex++] = coneHeight + shaftHeight; + + // Cone + for (let i = 0; i < numCircleVertices; i++) { + const angle = (i / numCircleVertices) * Math.PI * 2; + vertices[vertexIndex++] = Math.cos(angle) * coneRadius; + vertices[vertexIndex++] = Math.sin(angle) * coneRadius; + vertices[vertexIndex++] = shaftHeight; + + indices[triangleIndex++] = 0; + indices[triangleIndex++] = i + 1; + indices[triangleIndex++] = i === numCircleVertices - 1 ? 1 : i + 2; + } + + // The base of the cone + const baseStartIndex = vertexIndex / 3; + + vertices[vertexIndex++] = 0; + vertices[vertexIndex++] = 0; + vertices[vertexIndex++] = shaftHeight; + + for (let i = 0; i < numCircleVertices; i++) { + const angle = (i / numCircleVertices) * Math.PI * 2; + vertices[vertexIndex++] = Math.cos(angle) * coneRadius; + vertices[vertexIndex++] = Math.sin(angle) * coneRadius; + vertices[vertexIndex++] = shaftHeight; + + indices[triangleIndex++] = baseStartIndex; + indices[triangleIndex++] = baseStartIndex + i + 1; + indices[triangleIndex++] = i === numCircleVertices - 1 ? baseStartIndex + 1 : baseStartIndex + i + 2; + } + + // The shaft top of the arrow + const shaftTopStartIndex = vertexIndex / 3; + + vertices[vertexIndex++] = 0; + vertices[vertexIndex++] = 0; + vertices[vertexIndex++] = shaftHeight; + + for (let i = 0; i < numCircleVertices; i++) { + const angle = (i / numCircleVertices) * Math.PI * 2; + vertices[vertexIndex++] = Math.cos(angle) * shaftRadius; + vertices[vertexIndex++] = Math.sin(angle) * shaftRadius; + vertices[vertexIndex++] = shaftHeight; + } + + // The shaft bottom of the arrow + const shaftBottomStartIndex = vertexIndex / 3; + + vertices[vertexIndex++] = 0; + vertices[vertexIndex++] = 0; + vertices[vertexIndex++] = 0; + + for (let i = 0; i < numCircleVertices; i++) { + const angle = (i / numCircleVertices) * Math.PI * 2; + vertices[vertexIndex++] = Math.cos(angle) * shaftRadius; + vertices[vertexIndex++] = Math.sin(angle) * shaftRadius; + vertices[vertexIndex++] = 0; + + indices[triangleIndex++] = shaftBottomStartIndex; + indices[triangleIndex++] = shaftBottomStartIndex + i + 1; + indices[triangleIndex++] = + i === numCircleVertices - 1 ? shaftBottomStartIndex + 1 : shaftBottomStartIndex + i + 2; + } + + // The sides of the shaft + for (let i = 0; i < numCircleVertices; i++) { + indices[triangleIndex++] = shaftTopStartIndex + i + 1; + indices[triangleIndex++] = + i === numCircleVertices - 1 ? shaftBottomStartIndex + 1 : shaftBottomStartIndex + i + 2; + indices[triangleIndex++] = shaftBottomStartIndex + 1 + i; + + indices[triangleIndex++] = shaftTopStartIndex + i + 1; + indices[triangleIndex++] = i === numCircleVertices - 1 ? shaftTopStartIndex + 1 : shaftTopStartIndex + i + 2; + indices[triangleIndex++] = + i === numCircleVertices - 1 ? shaftBottomStartIndex + 1 : shaftBottomStartIndex + i + 2; + } + + return new Geometry({ + attributes: { + positions: vertices, + }, + topology: "triangle-list", + indices: indices, + }); +} + +export class DragHandleLayer extends CompositeLayer { + static layerName: string = "DragHandleLayer"; + + // @ts-expect-error - expected + state!: { + hoveredIndex: number; + }; + + initializeState(): void { + this.state = { + hoveredIndex: -1, + }; + } + + onHover(info: PickingInfo): boolean { + this.setState({ + hoveredIndex: info.index, + }); + + return true; + } + + renderLayers() { + const { position } = this.props; + const { hoveredIndex } = this.state; + + const layers = [ + new SimpleMeshLayer({ + id: "drag-handle-cone", + data: [{ position }], + mesh: makeArrowMesh(), + getPosition: (d) => d.position, + getColor: (d, ctx) => (ctx.index === hoveredIndex ? [255, 255, 255] : [0, 0, 255]), + getOrientation: [0, 0, 0], + getScale: [10, 10, 10], + pickable: true, + updateTriggers: { + getColor: [hoveredIndex], + }, + }), + ]; + + return layers; + } +} From 3568bf593b4e1cb3f31b7890c4a75b5f71425632 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 6 Feb 2025 18:14:34 +0100 Subject: [PATCH 37/97] wip --- .../view/components/ReadoutWrapper.tsx | 2 +- .../customDeckGlLayers/DragHandleLayer.ts | 41 +++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index 1e2841587..6dba6c5ca 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -145,7 +145,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { let adjustedLayers = [ ...props.layers, - new DragHandleLayer({ id: "est", position: [456000, 6818471, -500], dragDirection: DragDirection.X }), + new DragHandleLayer({ id: "est", position: [456000, 6818471, -500], dragDirection: DragDirection.Y }), ]; if (!gridVisible) { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); diff --git a/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts index 81e99082a..f7a785395 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts @@ -141,17 +141,52 @@ export class DragHandleLayer extends CompositeLayer { } renderLayers() { - const { position } = this.props; + const { position, dragDirection } = this.props; const { hoveredIndex } = this.state; + const data: { position: number[]; orientation: number[] }[] = []; + + if (dragDirection === DragDirection.X) { + data.push({ position: [position[0] + 10, position[1], position[2]], orientation: [90, 0, 0] }); + data.push({ position: [position[0] - 10, position[1], position[2]], orientation: [-90, 0, 0] }); + } else if (dragDirection === DragDirection.Y) { + data.push({ position: [position[0], position[1] + 10, position[2]], orientation: [90, 90, 0] }); + data.push({ position: [position[0], position[1] - 10, position[2]], orientation: [90, -90, 0] }); + } else if (dragDirection === DragDirection.Z) { + data.push({ position: [position[0], position[1], position[2] + 10], orientation: [0, 0, 0] }); + data.push({ position: [position[0], position[1], position[2] - 10], orientation: [180, 0, 0] }); + } else if (dragDirection === DragDirection.XY) { + data.push({ position: [position[0] + 10, position[1], position[2]], orientation: [90, 0, 0] }); + data.push({ position: [position[0] - 10, position[1], position[2]], orientation: [-90, 0, 0] }); + data.push({ position: [position[0], position[1] + 10, position[2]], orientation: [0, 90, 0] }); + data.push({ position: [position[0], position[1] - 10, position[2]], orientation: [0, -90, 0] }); + } else if (dragDirection === DragDirection.XZ) { + data.push({ position: [position[0] + 10, position[1], position[2]], orientation: [90, 0, 0] }); + data.push({ position: [position[0] - 10, position[1], position[2]], orientation: [-90, 0, 0] }); + data.push({ position: [position[0], position[1], position[2] + 10], orientation: [0, 0, 0] }); + data.push({ position: [position[0], position[1], position[2] - 10], orientation: [180, 0, 0] }); + } else if (dragDirection === DragDirection.YZ) { + data.push({ position: [position[0], position[1] + 10, position[2]], orientation: [0, 90, 0] }); + data.push({ position: [position[0], position[1] - 10, position[2]], orientation: [0, -90, 0] }); + data.push({ position: [position[0], position[1], position[2] + 10], orientation: [0, 0, 0] }); + data.push({ position: [position[0], position[1], position[2] - 10], orientation: [180, 0, 0] }); + } else if (dragDirection === DragDirection.XYZ) { + data.push({ position: [position[0] + 10, position[1], position[2]], orientation: [90, 0, 0] }); + data.push({ position: [position[0] - 10, position[1], position[2]], orientation: [-90, 0, 0] }); + data.push({ position: [position[0], position[1] + 10, position[2]], orientation: [0, 90, 0] }); + data.push({ position: [position[0], position[1] - 10, position[2]], orientation: [0, -90, 0] }); + data.push({ position: [position[0], position[1], position[2] + 10], orientation: [0, 0, 0] }); + data.push({ position: [position[0], position[1], position[2] - 10], orientation: [180, 0, 0] }); + } + const layers = [ new SimpleMeshLayer({ id: "drag-handle-cone", - data: [{ position }], + data, mesh: makeArrowMesh(), getPosition: (d) => d.position, getColor: (d, ctx) => (ctx.index === hoveredIndex ? [255, 255, 255] : [0, 0, 255]), - getOrientation: [0, 0, 0], + getOrientation: (d) => d.orientation, getScale: [10, 10, 10], pickable: true, updateTriggers: { From 53c37c515e7d274d5340bc62f07a9301724b10c1 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 6 Feb 2025 18:16:58 +0100 Subject: [PATCH 38/97] wip --- .../modules/3DViewerNew/view/components/ReadoutWrapper.tsx | 2 +- .../src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index 6dba6c5ca..20f4eac70 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -145,7 +145,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { let adjustedLayers = [ ...props.layers, - new DragHandleLayer({ id: "est", position: [456000, 6818471, -500], dragDirection: DragDirection.Y }), + new DragHandleLayer({ id: "est", position: [456000, 6818471, -500], dragDirection: DragDirection.XYZ }), ]; if (!gridVisible) { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); diff --git a/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts index f7a785395..600ee4456 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts @@ -173,8 +173,8 @@ export class DragHandleLayer extends CompositeLayer { } else if (dragDirection === DragDirection.XYZ) { data.push({ position: [position[0] + 10, position[1], position[2]], orientation: [90, 0, 0] }); data.push({ position: [position[0] - 10, position[1], position[2]], orientation: [-90, 0, 0] }); - data.push({ position: [position[0], position[1] + 10, position[2]], orientation: [0, 90, 0] }); - data.push({ position: [position[0], position[1] - 10, position[2]], orientation: [0, -90, 0] }); + data.push({ position: [position[0], position[1] + 10, position[2]], orientation: [90, 90, 0] }); + data.push({ position: [position[0], position[1] - 10, position[2]], orientation: [90, -90, 0] }); data.push({ position: [position[0], position[1], position[2] + 10], orientation: [0, 0, 0] }); data.push({ position: [position[0], position[1], position[2] - 10], orientation: [180, 0, 0] }); } From 3dc1a51a80fc1273019de90492b8cead19eab803 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 6 Feb 2025 20:42:40 +0100 Subject: [PATCH 39/97] wip --- .../makeRealizationSurfaceLayer.ts | 38 +++++++++++++++++++ .../view/components/LayersWrapper.tsx | 3 ++ 2 files changed, 41 insertions(+) create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts new file mode 100644 index 000000000..e6302e38f --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts @@ -0,0 +1,38 @@ +import { SurfaceDataPng_api } from "@api"; +import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; +import { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataTransforms"; +import { MapLayer } from "@webviz/subsurface-viewer/dist/layers"; + +import { RealizationSurfaceSettings } from "../customLayerImplementations/RealizationSurfaceLayer/types"; + +function isSurfaceDataFloat(apiData: SurfaceDataFloat_trans | SurfaceDataPng_api): apiData is SurfaceDataFloat_trans { + return Object.hasOwn(apiData, "valuesFloat32Arr"); +} + +export function makeRealizationSurfaceLayer({ + id, + name, + data, + colorScale, +}: VisualizationFunctionArgs): MapLayer { + if (!isSurfaceDataFloat(data)) { + throw new Error("SurfaceDataPng_api is not supported in makeRealizationSurfaceLayer"); + } + + return new MapLayer({ + id, + name, + meshData: data.valuesFloat32Arr, + frame: { + origin: [data.surface_def.origin_utm_x, data.surface_def.origin_utm_y], + count: [data.surface_def.npoints_x, data.surface_def.npoints_y], + increment: [data.surface_def.inc_x, data.surface_def.inc_y], + rotDeg: data.surface_def.rot_deg, + }, + valueRange: [data.value_min, data.value_max], + colorMapRange: [data.value_min, data.value_max], + colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max), + gridLines: false, + }); +} diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index cccc82996..367be1cff 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -8,6 +8,7 @@ import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { PendingWrapper } from "@lib/components/PendingWrapper"; import { useElementSize } from "@lib/hooks/useElementSize"; import { Rect3D, outerRectContainsInnerRect } from "@lib/utils/geometry"; +import { RealizationSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; import { IntersectionRealizationGridLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer"; @@ -17,6 +18,7 @@ import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFra import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; import { makeGrid3DLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer"; import { makeIntersectionLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer"; +import { makeRealizationSurfaceLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer"; import { Plane, makeSeismicFenceMeshLayerFunction, @@ -60,6 +62,7 @@ VISUALIZATION_FACTORY.registerVisualizationFunction( RealizationSeismicDepthSliceLayer, makeSeismicFenceMeshLayerFunction(Plane.DEPTH) ); +VISUALIZATION_FACTORY.registerVisualizationFunction(RealizationSurfaceLayer, makeRealizationSurfaceLayer); export type LayersWrapperProps = { layerManager: LayerManager; From 181e3f96ccb3d10808a3f259ef415f8edbc88a64 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 7 Feb 2025 10:24:37 +0100 Subject: [PATCH 40/97] wip --- .../makeSeismicFenceMeshLayer.ts | 14 + .../view/components/LayersWrapper.tsx | 9 +- .../view/utils/MoveableLayerPlugin.tsx | 524 ++++++++++++++++++ .../visualization/VisualizationFactory.ts | 5 +- .../SeismicFenceMeshLayer.ts | 41 +- 5 files changed, 585 insertions(+), 8 deletions(-) create mode 100644 frontend/src/modules/3DViewerNew/view/utils/MoveableLayerPlugin.tsx diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index 2c1e61adf..0a81a045a 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -79,6 +79,7 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { const properties = data.dataFloat32Arr; let startPosition: [number, number, number] = [0, 0, 0]; + let boundingBox: number[][] = []; let transformUVToXYZ: (u: number, v: number) => [number, number, number] = () => { throw new Error("transformUVToXYZ not implemented"); @@ -86,6 +87,12 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { if (plane === Plane.CROSSLINE || plane === Plane.INLINE) { startPosition = [bbox[0][0], bbox[0][1], -data.u_min]; + boundingBox = [ + startPosition, + [bbox[1][0], bbox[1][1], -data.u_min], + [bbox[1][0], bbox[1][1], -data.u_max], + [bbox[0][0], bbox[0][1], -data.u_max], + ]; transformUVToXYZ = (u: number, v: number): [number, number, number] => { const x = v * (bbox[1][0] - bbox[0][0]); const y = v * (bbox[1][1] - bbox[0][1]); @@ -94,6 +101,12 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { }; } else if (plane === Plane.DEPTH) { startPosition = [bbox[0][0], bbox[0][1], -settings.seismicDepthSlice]; + boundingBox = [ + startPosition, + [bbox[1][0], bbox[1][1], -settings.seismicDepthSlice], + [bbox[2][0], bbox[2][1], -settings.seismicDepthSlice], + [bbox[3][0], bbox[3][1], -settings.seismicDepthSlice], + ]; transformUVToXYZ = (u: number, v: number): [number, number, number] => { const x = u * (bbox[1][0] - bbox[0][0]) + v * (bbox[3][0] - bbox[0][0]); // Diagonal across bounding box const y = u * (bbox[1][1] - bbox[0][1]) + v * (bbox[3][1] - bbox[0][1]); // Diagonal across bounding box @@ -113,6 +126,7 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { }, startPosition, colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), + boundingBox, }); }; } diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 367be1cff..d6cef9337 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -44,6 +44,7 @@ import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; import { ReadoutWrapper } from "./ReadoutWrapper"; import { PlaceholderLayer } from "../../../_shared/customDeckGlLayers/PlaceholderLayer"; +import { DeckGlPlugin } from "../utils/DeckGlInstanceManager"; const VISUALIZATION_FACTORY = new VisualizationFactory(); VISUALIZATION_FACTORY.registerVisualizationFunction(DrilledWellborePicksLayer, makeWellborePicksLayer); @@ -110,7 +111,13 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { viewerLayers.push(...viewsAndLayers.layers); globalColorScales.push(...viewsAndLayers.colorScales); - const globalLayerIds = viewsAndLayers.layers.map((layer) => layer.layer.id); + const globalLayerIds: string[] = []; + const layers: string[]; + for (const el of viewsAndLayers.layers) { + if (el instanceof DeckGlPlugin) { + const las = el.getLayers(); + } + }; for (const view of viewsAndLayers.views) { viewports.push({ diff --git a/frontend/src/modules/3DViewerNew/view/utils/MoveableLayerPlugin.tsx b/frontend/src/modules/3DViewerNew/view/utils/MoveableLayerPlugin.tsx new file mode 100644 index 000000000..e94e98ddc --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/utils/MoveableLayerPlugin.tsx @@ -0,0 +1,524 @@ +import addPathIcon from "@assets/add_path.svg"; +import continuePathIcon from "@assets/continue_path.svg"; +import removePathIcon from "@assets/remove_path.svg"; +import { Layer, PickingInfo } from "@deck.gl/core"; +import { PublishSubscribe, PublishSubscribeDelegate } from "@modules/_shared/utils/PublishSubscribeDelegate"; +import { Edit, Remove } from "@mui/icons-material"; + +import { isEqual } from "lodash"; +import { v4 } from "uuid"; + +import { ContextMenuItem, DeckGlInstanceManager, DeckGlPlugin } from "./DeckGlInstanceManager"; + +import { + AllowHoveringOf, + EditablePolylineLayer, + isEditablePolylineLayerPickingInfo, +} from "../hooks/editablePolylines/deckGlLayers/EditablePolylineLayer"; +import { PolylinesLayer, isPolylinesLayerPickingInfo } from "../hooks/editablePolylines/deckGlLayers/PolylinesLayer"; + +export type Polyline = { + id: string; + name: string; + color: [number, number, number]; + path: number[][]; +}; + +export enum PolylineEditingMode { + DRAW = "draw", + ADD_POINT = "add_point", + REMOVE_POINT = "remove_point", + NONE = "none", + IDLE = "idle", +} + +export enum PolylinesPluginTopic { + EDITING_POLYLINE_ID = "editing_polyline_id", + EDITING_MODE = "editing_mode", + POLYLINES = "polylines", +} + +export type PolylinesPluginTopicPayloads = { + [PolylinesPluginTopic.EDITING_MODE]: PolylineEditingMode; + [PolylinesPluginTopic.EDITING_POLYLINE_ID]: string | null; + [PolylinesPluginTopic.POLYLINES]: Polyline[]; +}; + +enum AppendToPathLocation { + START = "start", + END = "end", +} + +function* defaultColorGenerator() { + const colors: [number, number, number][] = [ + [255, 0, 0], + [0, 255, 0], + [0, 0, 255], + [255, 255, 0], + [255, 0, 255], + [0, 255, 255], + ]; + + let index = 0; + while (true) { + yield colors[index]; + index = (index + 1) % colors.length; + } +} + +export class PolylinesPlugin extends DeckGlPlugin implements PublishSubscribe { + private _currentEditingPolylineId: string | null = null; + private _currentEditingPolylinePathReferencePointIndex: number | null = null; + private _polylines: Polyline[] = []; + private _editingMode: PolylineEditingMode = PolylineEditingMode.NONE; + private _draggedPathPointIndex: number | null = null; + private _appendToPathLocation: AppendToPathLocation = AppendToPathLocation.END; + private _selectedPolylineId: string | null = null; + private _hoverPoint: number[] | null = null; + private _colorGenerator: Generator<[number, number, number]>; + + private _publishSubscribeDelegate = new PublishSubscribeDelegate(); + + private setCurrentEditingPolylineId(id: string | null): void { + this._currentEditingPolylineId = id; + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.EDITING_POLYLINE_ID); + } + + constructor(manager: DeckGlInstanceManager, colorGenerator?: Generator<[number, number, number]>) { + super(manager); + this._colorGenerator = colorGenerator ?? defaultColorGenerator(); + } + + getActivePolyline(): Polyline | undefined { + return this._polylines.find((polyline) => polyline.id === this._currentEditingPolylineId); + } + + getPolylines(): Polyline[] { + return this._polylines; + } + + setPolylines(polylines: Polyline[]): void { + if (isEqual(this._polylines, polylines)) { + return; + } + this._polylines = polylines; + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.POLYLINES); + this.requireRedraw(); + } + + setActivePolylineName(name: string): void { + const activePolyline = this.getActivePolyline(); + if (!activePolyline) { + return; + } + + this._polylines = this._polylines.map((polyline) => { + if (polyline.id === activePolyline.id) { + return { + ...polyline, + name, + }; + } + return polyline; + }); + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.POLYLINES); + this.requireRedraw(); + } + + getPublishSubscribeDelegate(): PublishSubscribeDelegate { + return this._publishSubscribeDelegate; + } + + setEditingMode(mode: PolylineEditingMode): void { + this._editingMode = mode; + this._hoverPoint = null; + this._currentEditingPolylinePathReferencePointIndex = null; + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.EDITING_MODE); + this.requireRedraw(); + } + + getEditingMode(): PolylineEditingMode { + return this._editingMode; + } + + getCurrentEditingPolylineId(): string | null { + return this._currentEditingPolylineId; + } + + handleKeyUpEvent(key: string): void { + if (key === "Escape") { + if (this._editingMode === PolylineEditingMode.NONE) { + this._currentEditingPolylinePathReferencePointIndex = null; + this.requireRedraw(); + return; + } + if (this._editingMode === PolylineEditingMode.IDLE) { + this._currentEditingPolylinePathReferencePointIndex = null; + this._hoverPoint = null; + this.requireRedraw(); + return; + } + + this._hoverPoint = null; + this.setEditingMode(PolylineEditingMode.IDLE); + this.requireRedraw(); + return; + } + if (key === "Delete") { + if (this._editingMode === PolylineEditingMode.IDLE) { + if (this._selectedPolylineId) { + this._polylines = this._polylines.filter((polyline) => polyline.id !== this._selectedPolylineId); + this._selectedPolylineId = null; + this.requireRedraw(); + } + return; + } + } + } + + handleLayerClick(pickingInfo: PickingInfo): void { + if (this._editingMode === PolylineEditingMode.NONE || this._editingMode === PolylineEditingMode.IDLE) { + if (isPolylinesLayerPickingInfo(pickingInfo)) { + this._selectedPolylineId = pickingInfo.polylineId ?? null; + this.requireRedraw(); + } + return; + } + + if (!isEditablePolylineLayerPickingInfo(pickingInfo)) { + return; + } + + const activePolyline = this.getActivePolyline(); + if (!activePolyline) { + return; + } + + if (pickingInfo.editableEntity?.type === "point") { + if (![PolylineEditingMode.DRAW, PolylineEditingMode.REMOVE_POINT].includes(this._editingMode)) { + return; + } + + const index = pickingInfo.editableEntity.index; + if (this._editingMode === PolylineEditingMode.DRAW) { + if ( + (index === 0 || index === activePolyline.path.length - 1) && + this._currentEditingPolylinePathReferencePointIndex !== index + ) { + this._appendToPathLocation = index === 0 ? AppendToPathLocation.START : AppendToPathLocation.END; + this._currentEditingPolylinePathReferencePointIndex = index; + this.requireRedraw(); + return; + } + } + + const newPath = activePolyline.path.filter((_, i) => i !== index); + let newReferencePathPointIndex: number | null = null; + if (this._currentEditingPolylinePathReferencePointIndex !== null) { + newReferencePathPointIndex = Math.max(0, this._currentEditingPolylinePathReferencePointIndex - 1); + if (index > this._currentEditingPolylinePathReferencePointIndex) { + newReferencePathPointIndex = this._currentEditingPolylinePathReferencePointIndex; + } + if (activePolyline.path.length - 1 < 1) { + newReferencePathPointIndex = null; + } + } + + this.updateActivePolylinePath(newPath); + this._currentEditingPolylinePathReferencePointIndex = newReferencePathPointIndex; + this.requireRedraw(); + return; + } + + if (pickingInfo.editableEntity?.type === "line") { + if (![PolylineEditingMode.DRAW, PolylineEditingMode.ADD_POINT].includes(this._editingMode)) { + return; + } + + if (!pickingInfo.coordinate) { + return; + } + + const index = pickingInfo.editableEntity.index; + const newPath = [...activePolyline.path]; + newPath.splice(index + 1, 0, [...pickingInfo.coordinate]); + this.updateActivePolylinePath(newPath); + + let newReferencePathPointIndex: number | null = null; + if (this._currentEditingPolylinePathReferencePointIndex !== null) { + newReferencePathPointIndex = this._currentEditingPolylinePathReferencePointIndex + 1; + if (index > this._currentEditingPolylinePathReferencePointIndex) { + newReferencePathPointIndex = this._currentEditingPolylinePathReferencePointIndex; + } + } + + this._currentEditingPolylinePathReferencePointIndex = newReferencePathPointIndex; + this.requireRedraw(); + } + } + + private updateActivePolylinePath(newPath: number[][]): void { + const activePolyline = this.getActivePolyline(); + if (!activePolyline) { + return; + } + + this._polylines = this._polylines.map((polyline) => { + if (polyline.id === activePolyline.id) { + return { + ...polyline, + path: newPath, + }; + } + return polyline; + }); + + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.POLYLINES); + } + + handleClickAway(): void { + this._selectedPolylineId = null; + this.requireRedraw(); + } + + handleGlobalMouseHover(pickingInfo: PickingInfo): void { + if (this._editingMode !== PolylineEditingMode.DRAW) { + return; + } + + if (!pickingInfo.coordinate) { + return; + } + + this._hoverPoint = pickingInfo.coordinate; + this.requireRedraw(); + } + + handleGlobalMouseClick(pickingInfo: PickingInfo): boolean { + if (this._editingMode === PolylineEditingMode.NONE) { + return false; + } + + if (!pickingInfo.coordinate) { + return false; + } + + const activePolyline = this.getActivePolyline(); + if (!activePolyline && this._editingMode === PolylineEditingMode.DRAW) { + const id = v4(); + this._polylines.push({ + id, + name: "New polyline", + color: this._colorGenerator.next().value, + path: [[...pickingInfo.coordinate]], + }); + this._polylines = [...this._polylines]; + this._currentEditingPolylinePathReferencePointIndex = 0; + this.setCurrentEditingPolylineId(id); + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.POLYLINES); + this.requireRedraw(); + } else if (activePolyline) { + if (this._currentEditingPolylinePathReferencePointIndex === null) { + this.setCurrentEditingPolylineId(null); + this.setEditingMode(PolylineEditingMode.IDLE); + this.requireRedraw(); + return true; + } + + if (this._editingMode === PolylineEditingMode.DRAW) { + this.appendToActivePolylinePath(pickingInfo.coordinate); + this.requireRedraw(); + return true; + } + } + + return false; + } + + private appendToActivePolylinePath(point: number[]): void { + const activePolyline = this.getActivePolyline(); + if (!activePolyline) { + return; + } + + const newPath = [...activePolyline.path]; + if (this._appendToPathLocation === AppendToPathLocation.START) { + newPath.unshift(point); + this._currentEditingPolylinePathReferencePointIndex = 0; + } else { + newPath.push(point); + this._currentEditingPolylinePathReferencePointIndex = newPath.length - 1; + } + + this.updateActivePolylinePath(newPath); + } + + handleDragStart(pickingInfo: PickingInfo): void { + if (!isEditablePolylineLayerPickingInfo(pickingInfo)) { + return; + } + + if (pickingInfo.editableEntity?.type === "point") { + this._draggedPathPointIndex = pickingInfo.index; + this.requestDisablePanning(); + } + } + + handleDrag(pickingInfo: PickingInfo): void { + if (this._draggedPathPointIndex === null || !pickingInfo.coordinate) { + return; + } + + const activePolyline = this.getActivePolyline(); + if (!activePolyline) { + return; + } + + // Take first layer under cursor to get coordinates for the polyline point + // An alternative would be to store a reference to the layer the polyline was first created upon + // and always try to use that layer to get the coordinates + const layerUnderCursor = this.getFirstLayerUnderCursorInfo(pickingInfo.x, pickingInfo.y); + if (!layerUnderCursor || !layerUnderCursor.coordinate) { + return; + } + + const newPath = [...activePolyline.path]; + newPath[this._draggedPathPointIndex] = [...layerUnderCursor.coordinate]; + this.updateActivePolylinePath(newPath); + this.requireRedraw(); + } + + handleDragEnd(): void { + this._draggedPathPointIndex = null; + this.requestEnablePanning(); + } + + getCursor(pickingInfo: PickingInfo): string | null { + if (this._editingMode === PolylineEditingMode.NONE) { + return null; + } + + const activePolyline = this.getActivePolyline(); + + if (isEditablePolylineLayerPickingInfo(pickingInfo) && pickingInfo.editableEntity) { + if ( + [PolylineEditingMode.DRAW, PolylineEditingMode.ADD_POINT].includes(this._editingMode) && + pickingInfo.editableEntity.type === "line" + ) { + return `url(${addPathIcon}) 4 2, crosshair`; + } + + if ( + activePolyline && + [PolylineEditingMode.DRAW, PolylineEditingMode.REMOVE_POINT].includes(this._editingMode) && + pickingInfo.editableEntity.type === "point" + ) { + const index = pickingInfo.index; + if ( + (index === 0 || index === activePolyline.path.length - 1) && + index !== this._currentEditingPolylinePathReferencePointIndex && + this._editingMode === PolylineEditingMode.DRAW + ) { + return `url(${continuePathIcon}) 4 2, crosshair`; + } + + return `url(${removePathIcon}) 4 2, crosshair`; + } + + if (this._editingMode === PolylineEditingMode.IDLE && pickingInfo.editableEntity.type === "point") { + return "grab"; + } + } + + return "auto"; + } + + getContextMenuItems(pickingInfo: PickingInfo): ContextMenuItem[] { + if (this._editingMode !== PolylineEditingMode.IDLE) { + return []; + } + + if (!isPolylinesLayerPickingInfo(pickingInfo) || !pickingInfo.polylineId) { + return []; + } + + return [ + { + icon: , + label: "Edit", + onClick: () => { + this.setCurrentEditingPolylineId(pickingInfo.polylineId ?? null); + this.requireRedraw(); + }, + }, + { + icon: , + label: "Delete", + onClick: () => { + this._polylines = this._polylines.filter((polyline) => polyline.id !== pickingInfo.polylineId); + this.setCurrentEditingPolylineId(null); + this._publishSubscribeDelegate.notifySubscribers(PolylinesPluginTopic.POLYLINES); + this.requireRedraw(); + }, + }, + ]; + } + + getLayers(): Layer[] { + const layers: Layer[] = [ + new PolylinesLayer({ + id: "polylines-layer", + polylines: this._polylines.filter((polyline) => polyline.id !== this._currentEditingPolylineId), + selectedPolylineId: + this._editingMode === PolylineEditingMode.NONE ? undefined : this._selectedPolylineId ?? undefined, + hoverable: this._editingMode === PolylineEditingMode.IDLE, + }), + ]; + + let allowHoveringOf = AllowHoveringOf.NONE; + if ([PolylineEditingMode.DRAW, PolylineEditingMode.ADD_POINT].includes(this._editingMode)) { + allowHoveringOf = AllowHoveringOf.LINES_AND_POINTS; + } + if (this._editingMode === PolylineEditingMode.REMOVE_POINT) { + allowHoveringOf = AllowHoveringOf.POINTS; + } + + const activePolyline = this.getActivePolyline(); + if (activePolyline) { + layers.push( + new EditablePolylineLayer({ + id: "editable-polylines-layer", + polyline: activePolyline, + mouseHoverPoint: this._hoverPoint ?? undefined, + referencePathPointIndex: + this._editingMode === PolylineEditingMode.DRAW + ? this._currentEditingPolylinePathReferencePointIndex ?? undefined + : undefined, + onDragStart: this.handleDragStart.bind(this), + onDragEnd: this.handleDragEnd.bind(this), + allowHoveringOf, + }) + ); + } + + return layers; + } + + makeSnapshotGetter(topic: T): () => PolylinesPluginTopicPayloads[T] { + const snapshotGetter = (): any => { + if (topic === PolylinesPluginTopic.EDITING_MODE) { + return this._editingMode; + } + if (topic === PolylinesPluginTopic.EDITING_POLYLINE_ID) { + return this._currentEditingPolylineId; + } + if (topic === PolylinesPluginTopic.POLYLINES) { + return this._polylines; + } + + throw new Error(`Unknown topic ${topic}`); + }; + + return snapshotGetter; + } +} diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts index 90b1094a5..b92a81c82 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts @@ -3,6 +3,7 @@ import { Layer as EsvLayer } from "@equinor/esv-intersection"; import { StatusMessage } from "@framework/ModuleInstanceStatusController"; import { defaultColorPalettes, defaultContinuousSequentialColorPalettes } from "@framework/utils/colorPalettes"; import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; +import { DeckGlPlugin } from "@modules/3DViewerNew/view/utils/DeckGlInstanceManager"; import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; @@ -15,7 +16,7 @@ import { View } from "../framework/View/View"; import { BoundingBox, Layer, Settings, instanceofGroup, instanceofLayer } from "../interfaces"; export enum VisualizationTarget { - DECK_GL = "deck_gl_2d", + DECK_GL = "deck_gl", ESV = "esv", // VIDEX = "videx", } @@ -29,7 +30,7 @@ export type VisualizationFunctionArgs = { }; export type TargetReturnTypes = { - [VisualizationTarget.DECK_GL]: DeckGlLayer; + [VisualizationTarget.DECK_GL]: DeckGlLayer | DeckGlPlugin; [VisualizationTarget.ESV]: EsvLayer; }; diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts index 9238112ae..0792762a0 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts @@ -1,4 +1,5 @@ -import { CompositeLayer, CompositeLayerProps, Layer, UpdateParameters } from "@deck.gl/core"; +import { CompositeLayer, CompositeLayerProps, Layer, PickingInfo, UpdateParameters } from "@deck.gl/core"; +import { PathLayer } from "@deck.gl/layers"; import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; import { Geometry } from "@luma.gl/engine"; @@ -9,6 +10,7 @@ export type SeismicFenceMeshLayerProps = { indices: Uint32Array; properties: Float32Array; }; + boundingBox: number[][]; // [minX, minY, minZ, maxX, maxY, maxZ] colorMapFunction: (value: number) => [number, number, number]; }; @@ -18,6 +20,7 @@ export class SeismicFenceMeshLayer extends CompositeLayer>>) { super.updateState(params); - this.makeMesh(); + if (params.changeFlags.dataChanged) { + this.makeMesh(); + } } renderLayers() { - const { startPosition } = this.props; - const { geometry } = this.state; + const { startPosition, boundingBox } = this.props; + const { geometry, isHovered } = this.state; - return [ + const layers: Layer[] = [ new SimpleMeshLayer({ id: "seismic-fence-mesh-layer", data: [0], @@ -72,7 +82,28 @@ export class SeismicFenceMeshLayer extends CompositeLayer d, + getColor: [0, 0, 255, 255], + getWidth: 2, + pickable: false, + billboard: true, + widthUnits: "pixels", + parameters: { + depthTest: false, + }, + }) + ); + } + + return layers; } } From e3b26df77f306f22a9b8f5c7e133b1b7899005c5 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 10 Feb 2025 09:26:54 +0100 Subject: [PATCH 41/97] wip --- .../3DViewerNew/view/components/LayersWrapper.tsx | 9 +-------- .../LayerFramework/visualization/VisualizationFactory.ts | 3 +-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index d6cef9337..367be1cff 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -44,7 +44,6 @@ import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; import { ReadoutWrapper } from "./ReadoutWrapper"; import { PlaceholderLayer } from "../../../_shared/customDeckGlLayers/PlaceholderLayer"; -import { DeckGlPlugin } from "../utils/DeckGlInstanceManager"; const VISUALIZATION_FACTORY = new VisualizationFactory(); VISUALIZATION_FACTORY.registerVisualizationFunction(DrilledWellborePicksLayer, makeWellborePicksLayer); @@ -111,13 +110,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { viewerLayers.push(...viewsAndLayers.layers); globalColorScales.push(...viewsAndLayers.colorScales); - const globalLayerIds: string[] = []; - const layers: string[]; - for (const el of viewsAndLayers.layers) { - if (el instanceof DeckGlPlugin) { - const las = el.getLayers(); - } - }; + const globalLayerIds = viewsAndLayers.layers.map((layer) => layer.layer.id); for (const view of viewsAndLayers.views) { viewports.push({ diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts index b92a81c82..21d84cfe2 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts @@ -3,7 +3,6 @@ import { Layer as EsvLayer } from "@equinor/esv-intersection"; import { StatusMessage } from "@framework/ModuleInstanceStatusController"; import { defaultColorPalettes, defaultContinuousSequentialColorPalettes } from "@framework/utils/colorPalettes"; import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; -import { DeckGlPlugin } from "@modules/3DViewerNew/view/utils/DeckGlInstanceManager"; import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; @@ -30,7 +29,7 @@ export type VisualizationFunctionArgs = { }; export type TargetReturnTypes = { - [VisualizationTarget.DECK_GL]: DeckGlLayer | DeckGlPlugin; + [VisualizationTarget.DECK_GL]: DeckGlLayer; [VisualizationTarget.ESV]: EsvLayer; }; From 9b872f95ff685a3d7cfff92b9340468ea8eae233 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 10 Feb 2025 18:07:44 +0100 Subject: [PATCH 42/97] wip --- frontend/package-lock.json | 61 ++++++ frontend/package.json | 1 + .../view/components/LayersWrapper.tsx | 1 - .../view/components/ReadoutWrapper.tsx | 6 +- .../customDeckGlLayers/MeshLayer/MeshLayer.ts | 181 ++++++++++++++++++ .../MeshLayer/_shaders/fragment.glsl | 35 ++++ .../MeshLayer/_shaders/vertex.glsl | 44 +++++ .../customDeckGlLayers/MeshLayer/uniforms.ts | 23 +++ .../customDeckGlLayers/MeshLayer/utils.ts | 54 ++++++ .../SeismicFenceMeshLayer.ts | 29 ++- .../_shared/customDeckGlLayers/types.ts | 10 + frontend/vite.config.ts | 2 + 12 files changed, 435 insertions(+), 12 deletions(-) create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/uniforms.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/types.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 850596f98..8c1e71e7f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -79,6 +79,7 @@ "typescript": "^5.3.3", "vite": "^5.0.12", "vite-plugin-checker": "^0.6.0", + "vite-plugin-glsl": "^1.3.1", "vitest": "^1.1.3" } }, @@ -5151,6 +5152,49 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.2.tgz", @@ -16024,6 +16068,23 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/vite-plugin-glsl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vite-plugin-glsl/-/vite-plugin-glsl-1.3.1.tgz", + "integrity": "sha512-iClII8Idb9X0m6nS0YI2cWWXbBuT5EKKw5kXSAuRu4RJsNe4oypxKXE7jx0XMoyqij2s8WL0ZLfou801mpkREg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">= 16.15.1", + "npm": ">= 8.11.0" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + } + }, "node_modules/vite/node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index 949ba1840..35c86fe7e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -90,6 +90,7 @@ "typescript": "^5.3.3", "vite": "^5.0.12", "vite-plugin-checker": "^0.6.0", + "vite-plugin-glsl": "^1.3.1", "vitest": "^1.1.3" } } diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 367be1cff..b0f9ddd05 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -126,7 +126,6 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { "editable-polylines-layer", "polylines-layer", "hover-point-layer", - "est", ], }); viewerLayers.push(...view.layers); diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index 20f4eac70..ac2b70f35 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -7,7 +7,6 @@ import { WorkbenchSession } from "@framework/WorkbenchSession"; import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { IntersectionPolylinesEvent } from "@framework/userCreatedItems/IntersectionPolylines"; import { SubsurfaceViewerWithCameraState } from "@modules/_shared/components/SubsurfaceViewerWithCameraState"; -import { DragDirection, DragHandleLayer } from "@modules/_shared/customDeckGlLayers/DragHandleLayer"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { BoundingBox3D, LayerPickInfo, MapMouseEvent, ViewStateType, ViewsType } from "@webviz/subsurface-viewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; @@ -143,10 +142,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { setLayerPickingInfo(pickingInfo); } - let adjustedLayers = [ - ...props.layers, - new DragHandleLayer({ id: "est", position: [456000, 6818471, -500], dragDirection: DragDirection.XYZ }), - ]; + let adjustedLayers = [...props.layers]; if (!gridVisible) { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts new file mode 100644 index 000000000..f60cf2d7e --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts @@ -0,0 +1,181 @@ +import { + Accessor, + Color, + DefaultProps, + Layer, + LayerProps, + Material, + Position, + UpdateParameters, + picking, + project, +} from "@deck.gl/core"; +import { MeshAttribute, MeshAttributes, getMeshBoundingBox } from "@loaders.gl/schema"; +import { Geometry as GeometryType, Model } from "@luma.gl/engine"; +import { phongMaterial } from "@luma.gl/shadertools"; +import { utilities } from "@webviz/subsurface-viewer/dist/layers/shader_modules"; + +import fs from "./_shaders/fragment.glsl?raw"; +import vs from "./_shaders/vertex.glsl?raw"; +import { SimpleMeshProps, simpleMeshUniforms } from "./uniforms"; +import { getGeometry } from "./utils"; + +type Mesh = + | GeometryType + | { + attributes: MeshAttributes; + indices?: MeshAttribute; + } + | MeshAttributes; + +type _MeshLayerProps = { + mesh: string | Mesh | Promise | null; + getPosition?: Accessor; + getColor?: Accessor; + getOrientation?: Accessor; + getScale?: Accessor; + getTranslation?: Accessor; + sizeScale?: number; + material?: Material; + wireframe?: boolean; + // _instanced is a hack to use world position instead of meter offsets in mesh + // TODO - formalize API + _instanced: true; +}; + +export type MeshLayerProps = _MeshLayerProps & LayerProps; + +const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255]; + +const defaultProps: DefaultProps = { + mesh: { type: "object", value: null, async: true }, + getPosition: { type: "accessor", value: (d: any) => d.position }, + getColor: { type: "accessor", value: DEFAULT_COLOR }, + getOrientation: { type: "accessor", value: [0, 0, 0] }, + getScale: { type: "accessor", value: [1, 1, 1] }, + getTranslation: { type: "accessor", value: [0, 0, 0] }, + material: true, + wireframe: false, + _instanced: true, +}; + +export class MeshLayer extends Layer< + TExtraProps & Required<_MeshLayerProps> +> { + static layerName: string = "MeshLayer"; + static defaultProps = defaultProps; + + // @ts-expect-error - state is working in deck.gl + state!: { + model?: Model; + hasNormals?: boolean; + positionBounds?: [number[], number[]] | null; + }; + + getShaders() { + return super.getShaders({ + vs, + fs, + modules: [project, phongMaterial, picking, utilities, simpleMeshUniforms], + }); + } + + getBounds(): [number[], number[]] | null { + if (this.props._instanced) { + return super.getBounds(); + } + let result = this.state.positionBounds; + if (result) { + return result; + } + const { mesh } = this.props; + if (!mesh) { + return null; + } + // @ts-ignore Detect if mesh is generated by loaders.gl + result = mesh.header?.boundingBox; + + if (!result) { + // Otherwise, calculate bounding box from positions + const { attributes } = getGeometry(mesh as Mesh); + attributes.POSITION = attributes.POSITION || attributes.positions; + + //@ts-expect-error + result = getMeshBoundingBox(attributes); + } + + this.state.positionBounds = result; + return result; + } + + initializeState() { + const attributeManager = this.getAttributeManager(); + // attributeManager is always defined in a primitive layer + attributeManager!.addInstanced({ + instancePositions: { + transition: true, + type: "float64", + fp64: this.use64bitPositions(), + size: 3, + accessor: "getPosition", + }, + }); + } + + updateState(params: UpdateParameters) { + super.updateState(params); + + const { props, oldProps, changeFlags } = params; + if (props.mesh !== oldProps.mesh || changeFlags.extensionsChanged) { + this.state.positionBounds = null; + this.state.model?.destroy(); + if (props.mesh) { + this.state.model = this.getModel(props.mesh as Mesh); + + const attributes = (props.mesh as any).attributes || props.mesh; + this.setState({ + hasNormals: Boolean(attributes.NORMAL || attributes.normals), + }); + } + // attributeManager is always defined in a primitive layer + this.getAttributeManager()!.invalidateAll(); + } + + if (this.state.model) { + this.state.model.setTopology(this.props.wireframe ? "line-strip" : "triangle-list"); + } + } + + draw() { + const { model } = this.state; + if (!model) { + return; + } + + const { viewport, renderPass } = this.context; + const { sizeScale, coordinateSystem, _instanced } = this.props; + + const simpleMeshProps: SimpleMeshProps = { + sizeScale, + flatShading: !this.state.hasNormals, + }; + model.shaderInputs.setProps({ simpleMesh: simpleMeshProps }); + model.draw(renderPass); + } + + get isLoaded(): boolean { + return Boolean(this.state?.model && super.isLoaded); + } + + protected getModel(mesh: Mesh): Model { + const model = new Model(this.context.device, { + ...this.getShaders(), + id: this.props.id, + bufferLayout: this.getAttributeManager()!.getBufferLayouts(), + geometry: getGeometry(mesh), + isInstanced: true, + }); + + return model; + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl new file mode 100644 index 000000000..7c62fe0d6 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl @@ -0,0 +1,35 @@ +#version 300 es +#define SHADER_NAME mesh-layer-fs + +precision highp float; + +in vec3 cameraPosition; +in vec3 normals_commonspace; +in vec4 position_commonspace; +in vec4 vColor; +flat in int vertexIndex; + +out vec4 fragColor; + +void main(void) { + vec3 normal; + + if (simpleMesh.flatShading) { + normal = normalize(cross(dFdx(position_commonspace.xyz), dFdy(position_commonspace.xyz))); + } else { + normal = normals_commonspace; + } + + // Picking pass + if (picking.isActive > 0.5 && !(picking.isAttribute > 0.5)) { + fragColor = encodeVertexIndexToRGB(vertexIndex); + return; + } + + vec4 color = vec4(vColor.rgb, 1.0); + + DECKGL_FILTER_COLOR(color, geometry); + + vec3 lightColor = lighting_getLightColor(color.rgb, cameraPosition, position_commonspace.xyz, normal); + fragColor = vec4(lightColor, 1.0); +} \ No newline at end of file diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl new file mode 100644 index 000000000..b91aac9cd --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl @@ -0,0 +1,44 @@ +#version 300 es +#define SHADER_NAME mesh-layer-vs + +// Primitive attributes +in vec3 positions; +in vec3 normals; +in vec3 colors; + +// Instance attributes +in vec3 instancePositions; +in vec3 instancePositions64Low; +in vec3 instanceTranslation; + +// Outputs to fragment shader +out vec3 cameraPosition; +out vec3 normals_commonspace; +out vec4 position_commonspace; +out vec4 vColor; +flat out int vertexIndex; + + +void main(void) { + geometry.worldPosition = instancePositions; + geometry.pickingColor = vec3(1.0, 1.0, 0.0); + + vertexIndex = gl_VertexID; + + cameraPosition = project.cameraPosition; + + vColor = vec4(colors.rgb, 1.0); + + vec3 pos = positions * simpleMesh.sizeScale + instanceTranslation; + + pos = project_size(pos); + DECKGL_FILTER_SIZE(pos, geometry); + gl_Position = project_position_to_clipspace(instancePositions, instancePositions64Low, pos, position_commonspace); + geometry.position = position_commonspace; + normals_commonspace = project_normal(normals); + + geometry.normal = normals_commonspace; + DECKGL_FILTER_GL_POSITION(gl_Position, geometry); + + DECKGL_FILTER_COLOR(vColor, geometry); +} \ No newline at end of file diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/uniforms.ts b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/uniforms.ts new file mode 100644 index 000000000..d44438267 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/uniforms.ts @@ -0,0 +1,23 @@ +import type { ShaderModule } from "@luma.gl/shadertools"; + +const uniformBlock = `\ +uniform simpleMeshUniforms { + float sizeScale; + bool flatShading; +} simpleMesh; +`; + +export type SimpleMeshProps = { + sizeScale?: number; + flatShading?: boolean; +}; + +export const simpleMeshUniforms = { + name: "simpleMesh", + vs: uniformBlock, + fs: uniformBlock, + uniformTypes: { + sizeScale: "f32", + flatShading: "f32", + }, +} as const satisfies ShaderModule; diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts new file mode 100644 index 000000000..1fbb37e60 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts @@ -0,0 +1,54 @@ +import { log } from "@deck.gl/core"; +import { MeshAttributes } from "@loaders.gl/schema"; +import { Geometry } from "@luma.gl/engine"; + +import { Mesh } from "../types"; + +function normalizeGeometryAttributes(attributes: MeshAttributes): MeshAttributes { + const positionAttribute = attributes.positions || attributes.POSITION; + log.assert(positionAttribute, 'no "postions" or "POSITION" attribute in mesh'); + + const vertexCount = positionAttribute.value.length / positionAttribute.size; + let colorAttribute = attributes.COLOR_0 || attributes.colors; + if (!colorAttribute) { + colorAttribute = { size: 3, value: new Float32Array(vertexCount * 3).fill(1) }; + } + let normalAttribute = attributes.NORMAL || attributes.normals; + if (!normalAttribute) { + normalAttribute = { size: 3, value: new Float32Array(vertexCount * 3).fill(0) }; + } + let texCoordAttribute = attributes.TEXCOORD_0 || attributes.texCoords; + if (!texCoordAttribute) { + texCoordAttribute = { size: 2, value: new Float32Array(vertexCount * 2).fill(0) }; + } + + return { + positions: positionAttribute, + colors: colorAttribute, + normals: normalAttribute, + texCoords: texCoordAttribute, + }; +} + +/* + * Convert mesh data into geometry + * @returns {Geometry} geometry + */ +export function getGeometry(data: Mesh): Geometry { + if (data instanceof Geometry) { + // @ts-expect-error data.attributes is readonly + data.attributes = normalizeGeometryAttributes(data.attributes); + return data; + } else if ((data as any).attributes) { + return new Geometry({ + ...data, + topology: "triangle-list", + attributes: normalizeGeometryAttributes((data as any).attributes), + }); + } else { + return new Geometry({ + topology: "triangle-list", + attributes: normalizeGeometryAttributes(data as MeshAttributes), + }); + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts index 0792762a0..b42944c17 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts @@ -1,8 +1,16 @@ -import { CompositeLayer, CompositeLayerProps, Layer, PickingInfo, UpdateParameters } from "@deck.gl/core"; +import { + CompositeLayer, + CompositeLayerProps, + GetPickingInfoParams, + Layer, + PickingInfo, + UpdateParameters, +} from "@deck.gl/core"; import { PathLayer } from "@deck.gl/layers"; -import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; import { Geometry } from "@luma.gl/engine"; +import { MeshLayer } from "./MeshLayer/MeshLayer"; + export type SeismicFenceMeshLayerProps = { startPosition: [number, number, number]; data: { @@ -12,6 +20,11 @@ export type SeismicFenceMeshLayerProps = { }; boundingBox: number[][]; // [minX, minY, minZ, maxX, maxY, maxZ] colorMapFunction: (value: number) => [number, number, number]; + hoverable?: boolean; +}; + +export type SeismicFenceMeshLayerPickingInfo = PickingInfo & { + propertyValue: number; }; export class SeismicFenceMeshLayer extends CompositeLayer { @@ -42,7 +55,6 @@ export class SeismicFenceMeshLayer extends CompositeLayer[] = [ - new SimpleMeshLayer({ + new MeshLayer({ id: "seismic-fence-mesh-layer", data: [0], mesh: geometry, @@ -86,7 +103,7 @@ export class SeismicFenceMeshLayer extends CompositeLayer { }, }), vitePluginChecker({ typescript: true }), + glsl(), ], build: { rollupOptions: { From 15557a5d6f0deb51ade959e88a6e28b5a72fc2d9 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 10 Feb 2025 19:39:33 +0100 Subject: [PATCH 43/97] wip --- .../customDeckGlLayers/MeshLayer/MeshLayer.ts | 29 +++++++------------ .../MeshLayer/_shaders/fragment.glsl | 6 ++-- .../MeshLayer/_shaders/vertex.glsl | 7 +++-- .../customDeckGlLayers/MeshLayer/uniforms.ts | 23 --------------- .../customDeckGlLayers/MeshLayer/utils.ts | 5 ---- 5 files changed, 20 insertions(+), 50 deletions(-) delete mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/uniforms.ts diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts index f60cf2d7e..d3f90173d 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts @@ -8,25 +8,18 @@ import { Position, UpdateParameters, picking, - project, + project32, } from "@deck.gl/core"; -import { MeshAttribute, MeshAttributes, getMeshBoundingBox } from "@loaders.gl/schema"; -import { Geometry as GeometryType, Model } from "@luma.gl/engine"; +import { getMeshBoundingBox } from "@loaders.gl/schema"; +import { Model } from "@luma.gl/engine"; import { phongMaterial } from "@luma.gl/shadertools"; import { utilities } from "@webviz/subsurface-viewer/dist/layers/shader_modules"; import fs from "./_shaders/fragment.glsl?raw"; import vs from "./_shaders/vertex.glsl?raw"; -import { SimpleMeshProps, simpleMeshUniforms } from "./uniforms"; import { getGeometry } from "./utils"; -type Mesh = - | GeometryType - | { - attributes: MeshAttributes; - indices?: MeshAttribute; - } - | MeshAttributes; +import { Mesh } from "../types"; type _MeshLayerProps = { mesh: string | Mesh | Promise | null; @@ -76,7 +69,7 @@ export class MeshLayer extends Layer< return super.getShaders({ vs, fs, - modules: [project, phongMaterial, picking, utilities, simpleMeshUniforms], + modules: [project32, phongMaterial, picking, utilities], }); } @@ -146,20 +139,20 @@ export class MeshLayer extends Layer< } } - draw() { + draw({ uniforms }: { uniforms: any }) { const { model } = this.state; if (!model) { return; } - const { viewport, renderPass } = this.context; - const { sizeScale, coordinateSystem, _instanced } = this.props; + const { renderPass } = this.context; + const { sizeScale } = this.props; - const simpleMeshProps: SimpleMeshProps = { + model.setUniforms(uniforms); + model.setUniforms({ sizeScale, flatShading: !this.state.hasNormals, - }; - model.shaderInputs.setProps({ simpleMesh: simpleMeshProps }); + }); model.draw(renderPass); } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl index 7c62fe0d6..5b30bd1ef 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl +++ b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl @@ -8,13 +8,15 @@ in vec3 normals_commonspace; in vec4 position_commonspace; in vec4 vColor; flat in int vertexIndex; +uniform bool flatShading; +uniform float opacity; out vec4 fragColor; void main(void) { vec3 normal; - if (simpleMesh.flatShading) { + if (flatShading) { normal = normalize(cross(dFdx(position_commonspace.xyz), dFdy(position_commonspace.xyz))); } else { normal = normals_commonspace; @@ -31,5 +33,5 @@ void main(void) { DECKGL_FILTER_COLOR(color, geometry); vec3 lightColor = lighting_getLightColor(color.rgb, cameraPosition, position_commonspace.xyz, normal); - fragColor = vec4(lightColor, 1.0); + fragColor = vec4(lightColor, color.a * opacity); } \ No newline at end of file diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl index b91aac9cd..298b34156 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl +++ b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl @@ -1,6 +1,9 @@ #version 300 es #define SHADER_NAME mesh-layer-vs +// Scale the model +uniform float sizeScale; + // Primitive attributes in vec3 positions; in vec3 normals; @@ -25,11 +28,11 @@ void main(void) { vertexIndex = gl_VertexID; - cameraPosition = project.cameraPosition; + cameraPosition = project_uCameraPosition; vColor = vec4(colors.rgb, 1.0); - vec3 pos = positions * simpleMesh.sizeScale + instanceTranslation; + vec3 pos = positions * sizeScale + instanceTranslation; pos = project_size(pos); DECKGL_FILTER_SIZE(pos, geometry); diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/uniforms.ts b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/uniforms.ts deleted file mode 100644 index d44438267..000000000 --- a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/uniforms.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { ShaderModule } from "@luma.gl/shadertools"; - -const uniformBlock = `\ -uniform simpleMeshUniforms { - float sizeScale; - bool flatShading; -} simpleMesh; -`; - -export type SimpleMeshProps = { - sizeScale?: number; - flatShading?: boolean; -}; - -export const simpleMeshUniforms = { - name: "simpleMesh", - vs: uniformBlock, - fs: uniformBlock, - uniformTypes: { - sizeScale: "f32", - flatShading: "f32", - }, -} as const satisfies ShaderModule; diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts index 1fbb37e60..3fbacd566 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts @@ -17,16 +17,11 @@ function normalizeGeometryAttributes(attributes: MeshAttributes): MeshAttributes if (!normalAttribute) { normalAttribute = { size: 3, value: new Float32Array(vertexCount * 3).fill(0) }; } - let texCoordAttribute = attributes.TEXCOORD_0 || attributes.texCoords; - if (!texCoordAttribute) { - texCoordAttribute = { size: 2, value: new Float32Array(vertexCount * 2).fill(0) }; - } return { positions: positionAttribute, colors: colorAttribute, normals: normalAttribute, - texCoords: texCoordAttribute, }; } From 4eee5a333577aa2c33a48001db33b9fe7a4b0d6c Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 10 Feb 2025 21:06:14 +0100 Subject: [PATCH 44/97] wip --- .../makeSeismicFenceMeshLayer.ts | 3 ++ .../ExtendedSimpleMeshLayer.ts | 43 ++++++++++++++++++ .../SeismicFenceMeshLayer.ts | 44 +++++++++++++++---- 3 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer.ts diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index 0a81a045a..614e31cb1 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -71,6 +71,7 @@ export enum Plane { export function makeSeismicFenceMeshLayerFunction(plane: Plane) { return function makeSeismicFenceMeshLayer({ id, + name, data, colorScale, settings, @@ -119,6 +120,7 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { return new SeismicFenceMeshLayer({ id, + name, data: { vertices, indices, @@ -127,6 +129,7 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { startPosition, colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), boundingBox, + zIncreaseDownwards: true, }); }; } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer.ts new file mode 100644 index 000000000..219a9898a --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer.ts @@ -0,0 +1,43 @@ +import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; + +export class ExtendedSimpleMeshLayer extends SimpleMeshLayer { + getShaders() { + return { + ...super.getShaders(), + inject: { + "vs:#decl": ` + flat out int vertexIndex;`, + "vs:#main-end": ` + vertexIndex = gl_VertexID;`, + "fs:#decl": ` + flat in int vertexIndex; + + vec4 encodeVertexIndexToRGB (int vertexIndex) { + float r = 0.0; + float g = 0.0; + float b = 0.0; + + if (vertexIndex >= (256 * 256) - 1) { + r = floor(float(vertexIndex) / (256.0 * 256.0)); + vertexIndex -= int(r * (256.0 * 256.0)); + } + + if (vertexIndex >= 256 - 1) { + g = floor(float(vertexIndex) / 256.0); + vertexIndex -= int(g * 256.0); + } + + b = float(vertexIndex); + + return vec4(r / 255.0, g / 255.0, b / 255.0, 1.0); + } + `, + "fs:#main-start": ` + if (picking.isActive > 0.5 && !(picking.isAttribute > 0.5)) { + fragColor = encodeVertexIndexToRGB(vertexIndex); + return; + }`, + }, + }; + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts index b42944c17..088673ced 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts @@ -9,9 +9,14 @@ import { import { PathLayer } from "@deck.gl/layers"; import { Geometry } from "@luma.gl/engine"; -import { MeshLayer } from "./MeshLayer/MeshLayer"; +import { ExtendedSimpleMeshLayer } from "./ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer"; + +export type SeismicFenceMeshLayerPickingInfo = { + properties?: { name: string; value: number }[]; +} & PickingInfo; export type SeismicFenceMeshLayerProps = { + name?: string; startPosition: [number, number, number]; data: { vertices: Float32Array; @@ -21,10 +26,7 @@ export type SeismicFenceMeshLayerProps = { boundingBox: number[][]; // [minX, minY, minZ, maxX, maxY, maxZ] colorMapFunction: (value: number) => [number, number, number]; hoverable?: boolean; -}; - -export type SeismicFenceMeshLayerPickingInfo = PickingInfo & { - propertyValue: number; + zIncreaseDownwards?: boolean; }; export class SeismicFenceMeshLayer extends CompositeLayer { @@ -70,9 +72,33 @@ export class SeismicFenceMeshLayer extends CompositeLayer[] = [ - new MeshLayer({ + new ExtendedSimpleMeshLayer({ id: "seismic-fence-mesh-layer", data: [0], mesh: geometry, From f739d1281bbff456ecf9a7ef71b3e15e8f081acb Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 11 Feb 2025 00:19:36 +0100 Subject: [PATCH 45/97] wip --- .../makeSeismicFenceMeshLayer.ts | 37 +++++++++---------- .../customDeckGlLayers/MovableLayerWrapper.ts | 33 +++++++++++++++++ .../SeismicFenceMeshLayer.ts | 2 +- 3 files changed, 51 insertions(+), 21 deletions(-) create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index 614e31cb1..64ddf2c5f 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -1,6 +1,8 @@ +import { Layer } from "@deck.gl/core"; import { SeismicSliceData_trans } from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; +import { MovableLayerWrapper } from "@modules/_shared/customDeckGlLayers/MovableLayerWrapper"; import { SeismicFenceMeshLayer } from "@modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer"; /* @@ -34,13 +36,6 @@ function generatePointFenceMesh( let indicesIndex = 0; for (let v = 0; v < numSamplesV; v++) { - /* - if (i > 1) { - // Draw a degenerated triangle to move to the next row - indices[indicesIndex++] = (i - 1) * numSamplesXY + numSamplesXY - 1; - indices[indicesIndex++] = i * numSamplesXY; - } - */ for (let u = 0; u < numSamplesU; u++) { const [x, y, z] = transformUVToXYZ(u * stepU, v * stepV); vertices[verticesIndex++] = x; @@ -75,7 +70,7 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { data, colorScale, settings, - }: VisualizationFunctionArgs): SeismicFenceMeshLayer { + }: VisualizationFunctionArgs): Layer { const bbox = data.bbox_utm; const properties = data.dataFloat32Arr; @@ -118,18 +113,20 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { const { vertices, indices } = generatePointFenceMesh(data.u_num_samples, data.v_num_samples, transformUVToXYZ); - return new SeismicFenceMeshLayer({ - id, - name, - data: { - vertices, - indices, - properties, - }, - startPosition, - colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), - boundingBox, - zIncreaseDownwards: true, + return new MovableLayerWrapper({ + wrappedLayer: new SeismicFenceMeshLayer({ + id, + name, + data: { + vertices, + indices, + properties, + }, + startPosition, + colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), + boundingBox, + zIncreaseDownwards: true, + }), }); }; } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts b/frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts new file mode 100644 index 000000000..a96129875 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts @@ -0,0 +1,33 @@ +import { CompositeLayer, GetPickingInfoParams, Layer, LayersList, PickingInfo } from "@deck.gl/core"; + +import { DragDirection } from "./DragHandleLayer"; + +export type MovableLayerWrapperProps, TProps extends {}> = { + wrappedLayer: TLayer; + draggable?: boolean; + dragDirection?: DragDirection; +} & TProps; + +export type MovableLayerPickingInfo = { + isMovable: boolean; +} & PickingInfo; + +export class MovableLayerWrapper, TProps extends {}> extends CompositeLayer< + MovableLayerWrapperProps +> { + static layerName: string = "MovableLayerWrapper"; + + getPickingInfo({ info }: GetPickingInfoParams): MovableLayerPickingInfo { + const { wrappedLayer } = this.props; + return { + ...info, + layer: wrappedLayer, + isMovable: true, + }; + } + + renderLayers(): Layer | null | LayersList { + const { wrappedLayer } = this.props; + return wrappedLayer; + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts index 088673ced..ec4519a23 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts @@ -83,7 +83,7 @@ export class SeismicFenceMeshLayer extends CompositeLayer Date: Tue, 11 Feb 2025 17:17:12 +0100 Subject: [PATCH 46/97] wip --- .../primary/routers/_shared/schemas.py | 12 +++++ .../primary/primary/routers/grid3d/schemas.py | 13 +---- .../primary/routers/seismic/converters.py | 37 +++++++++++++ .../primary/primary/routers/seismic/router.py | 2 +- .../primary/routers/seismic/schemas.py | 46 +++++++++++----- .../services/sumo_access/seismic_access.py | 25 +++++---- .../services/sumo_access/seismic_types.py | 36 +++++++++---- frontend/src/api/autogen/types.gen.ts | 41 +++++++++----- .../ObservedSurfaceSettingsContext.ts | 16 +++--- .../RealizationGridSettingsContext.ts | 22 ++++---- .../RealizationPolygonsSettingsContext.ts | 5 +- .../RealizationSurfaceSettingsContext.ts | 5 +- .../StatisticalSurfaceSettingsContext.ts | 5 +- ...ersectionRealizationGridSettingsContext.ts | 5 +- .../RealizationGridSettingsContext.ts | 26 ++++----- .../RealizationSeismicCrosslineLayer.ts | 54 +++++++++++++++++-- ...lizationSeismicCrosslineSettingsContext.ts | 42 ++++++++++----- .../RealizationSeismicCrosslineLayer/types.ts | 5 ++ ...izationSeismicDepthSliceSettingsContext.ts | 23 ++++---- ...RealizationSeismicInlineSettingsContext.ts | 19 +++---- .../RealizationSurfaceSettingsContext.ts | 5 +- .../makeIntersectionGrid3dLayer.ts | 5 +- .../makeSeismicFenceMeshLayer.ts | 11 +++- .../view/components/LayersWrapper.tsx | 26 ++++----- .../components/layerSettings/seismicLayer.tsx | 22 ++++---- .../LayerFramework/delegates/LayerDelegate.ts | 48 ++++++++++++++--- .../delegates/SettingsContextDelegate.ts | 48 ++++++++++++++--- .../delegates/_utils/Dependency.ts | 4 +- .../_shared/LayerFramework/interfaces.ts | 41 ++++++++++---- .../LayerFramework/layers/LayerComponent.tsx | 2 +- .../LayerFramework/layers/LayerRegistry.ts | 7 +-- .../DrilledWellTrajectoriesSettingsContext.ts | 5 +- .../DrilledWellborePicksSettingsContext.ts | 5 +- .../visualization/VisualizationFactory.ts | 20 ++++--- .../ExtendedSimpleMeshLayer.ts | 1 + .../customDeckGlLayers/MovableLayerWrapper.ts | 11 ++-- .../SeismicFenceMeshLayer.ts | 42 ++++++++++----- 37 files changed, 496 insertions(+), 246 deletions(-) create mode 100644 backend_py/primary/primary/routers/_shared/schemas.py diff --git a/backend_py/primary/primary/routers/_shared/schemas.py b/backend_py/primary/primary/routers/_shared/schemas.py new file mode 100644 index 000000000..e508d0f3b --- /dev/null +++ b/backend_py/primary/primary/routers/_shared/schemas.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel + + +class BoundingBox3d(BaseModel): + """Bounding box for a 3D grid geometry""" + + xmin: float + ymin: float + zmin: float + xmax: float + ymax: float + zmax: float diff --git a/backend_py/primary/primary/routers/grid3d/schemas.py b/backend_py/primary/primary/routers/grid3d/schemas.py index 7c5d30bb8..2449bc14f 100644 --- a/backend_py/primary/primary/routers/grid3d/schemas.py +++ b/backend_py/primary/primary/routers/grid3d/schemas.py @@ -3,6 +3,8 @@ from pydantic import BaseModel from webviz_pkg.core_utils.b64 import B64FloatArray, B64UintArray +from .._shared.schemas import BoundingBox3d + # Rename? class Grid3dGeometry(BaseModel): @@ -26,17 +28,6 @@ class Grid3dMappedProperty(BaseModel): max_grid_prop_value: float -class BoundingBox3d(BaseModel): - """Bounding box for a 3D grid geometry""" - - xmin: float - ymin: float - zmin: float - xmax: float - ymax: float - zmax: float - - class Grid3dZone(BaseModel): """Named subset of 3D grid layers (Zone)""" diff --git a/backend_py/primary/primary/routers/seismic/converters.py b/backend_py/primary/primary/routers/seismic/converters.py index 56ab62f1c..a5420c0fe 100644 --- a/backend_py/primary/primary/routers/seismic/converters.py +++ b/backend_py/primary/primary/routers/seismic/converters.py @@ -3,9 +3,46 @@ from webviz_pkg.core_utils.b64 import b64_encode_float_array_as_float32 from primary.services.vds_access.response_types import VdsSliceMetadata +from primary.services.sumo_access.seismic_types import SeismicCubeMeta from . import schemas +def to_api_vds_cube_meta(seismic_meta: SeismicCubeMeta) -> schemas.SeismicCubeMeta: + """ + Create API SeismicCubeMeta from VdsSliceMetadata + """ + + return schemas.SeismicCubeMeta( + seismicAttribute=seismic_meta.seismic_attribute, + unit=seismic_meta.unit, + isoDateOrInterval=seismic_meta.iso_date_or_interval, + isObservation=seismic_meta.is_observation, + isDepth=seismic_meta.is_depth, + bbox=schemas.BoundingBox3d( + xmin=seismic_meta.bbox.xmin, + ymin=seismic_meta.bbox.ymin, + zmin=seismic_meta.bbox.zmin, + xmax=seismic_meta.bbox.xmax, + ymax=seismic_meta.bbox.ymax, + zmax=seismic_meta.bbox.zmax, + ), + spec=schemas.SeismicCubeSpec( + numCols=seismic_meta.spec.num_cols, + numRows=seismic_meta.spec.num_rows, + numLayers=seismic_meta.spec.num_layers, + xOrigin=seismic_meta.spec.x_origin, + yOrigin=seismic_meta.spec.y_origin, + zOrigin=seismic_meta.spec.z_origin, + xInc=seismic_meta.spec.x_inc, + yInc=seismic_meta.spec.y_inc, + zInc=seismic_meta.spec.z_inc, + yFlip=seismic_meta.spec.y_flip, + zFlip=seismic_meta.spec.z_flip, + rotation=seismic_meta.spec.rotation, + ), + ) + + def to_api_vds_slice_data( flattened_slice_traces_array: NDArray[np.float32], metadata: VdsSliceMetadata ) -> schemas.SeismicSliceData: diff --git a/backend_py/primary/primary/routers/seismic/router.py b/backend_py/primary/primary/routers/seismic/router.py index 334c785bc..c0478c845 100644 --- a/backend_py/primary/primary/routers/seismic/router.py +++ b/backend_py/primary/primary/routers/seismic/router.py @@ -32,7 +32,7 @@ async def get_seismic_cube_meta_list( ) seismic_cube_meta_list = await access.get_seismic_cube_meta_list_async() try: - return [schemas.SeismicCubeMeta(**meta.__dict__) for meta in seismic_cube_meta_list] + return [converters.to_api_vds_cube_meta(meta) for meta in seismic_cube_meta_list] except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc diff --git a/backend_py/primary/primary/routers/seismic/schemas.py b/backend_py/primary/primary/routers/seismic/schemas.py index 9f2841084..c371d7b02 100644 --- a/backend_py/primary/primary/routers/seismic/schemas.py +++ b/backend_py/primary/primary/routers/seismic/schemas.py @@ -1,24 +1,42 @@ from typing import List -from enum import StrEnum from pydantic import BaseModel from webviz_pkg.core_utils.b64 import B64FloatArray +from .._shared.schemas import BoundingBox3d + + +class SeismicCubeSpec(BaseModel): + """ + Specification for a seismic cube. + """ + + numCols: int + numRows: int + numLayers: int + xOrigin: float + yOrigin: float + zOrigin: float + xInc: float + yInc: float + zInc: float + yFlip: int + zFlip: int + rotation: float + class SeismicCubeMeta(BaseModel): - seismic_attribute: str - iso_date_or_interval: str - is_observation: bool - is_depth: bool - i_min: int - i_max: int - j_min: int - j_max: int - k_min: int - k_max: int - z_min: float - z_max: float - z_inc: float + """ + Metadata for a seismic cube. + """ + + seismicAttribute: str + unit: str + isoDateOrInterval: str + isObservation: bool + isDepth: bool + bbox: BoundingBox3d + spec: SeismicCubeSpec class SeismicFencePolyline(BaseModel): diff --git a/backend_py/primary/primary/services/sumo_access/seismic_access.py b/backend_py/primary/primary/services/sumo_access/seismic_access.py index 7ba0f6b9c..0f315e990 100644 --- a/backend_py/primary/primary/services/sumo_access/seismic_access.py +++ b/backend_py/primary/primary/services/sumo_access/seismic_access.py @@ -44,18 +44,25 @@ async def get_seismic_cube_meta_list_async(self) -> List[SeismicCubeMeta]: iso_string_or_time_interval = f"{t_start}/{t_end}" seismic_meta = SeismicCubeMeta( seismic_attribute=cube["data"].get("tagname"), + unit=cube["data"].get("unit"), iso_date_or_interval=iso_string_or_time_interval, is_observation=cube["data"]["is_observation"], is_depth=cube["data"].get("vertical_domain", "depth") == "depth", - i_min=0, - i_max=cube["data"]["spec"]["ncol"]-1, - j_min=0, - j_max=cube["data"]["spec"]["nrow"]-1, - k_min=0, - k_max=cube["data"]["spec"]["nlay"]-1, - z_min=cube["data"]["bbox"]["zmin"], - z_max=cube["data"]["bbox"]["zmax"], - z_inc=cube["data"]["spec"]["zinc"], + bbox=cube["data"]["bbox"], + spec={ + "num_cols": cube["data"]["spec"]["ncol"], + "num_rows": cube["data"]["spec"]["nrow"], + "num_layers": cube["data"]["spec"]["nlay"], + "x_origin": cube["data"]["spec"]["xori"], + "y_origin": cube["data"]["spec"]["yori"], + "z_origin": cube["data"]["spec"]["zori"], + "x_inc": cube["data"]["spec"]["xinc"], + "y_inc": cube["data"]["spec"]["yinc"], + "z_inc": cube["data"]["spec"]["zinc"], + "y_flip": cube["data"]["spec"]["yflip"], + "z_flip": cube["data"]["spec"]["zflip"], + "rotation": cube["data"]["spec"]["rotation"], + }, ) seismic_cube_meta_list.append(seismic_meta) return seismic_cube_meta_list diff --git a/backend_py/primary/primary/services/sumo_access/seismic_types.py b/backend_py/primary/primary/services/sumo_access/seismic_types.py index 24a0d6791..72496a192 100644 --- a/backend_py/primary/primary/services/sumo_access/seismic_types.py +++ b/backend_py/primary/primary/services/sumo_access/seismic_types.py @@ -1,20 +1,38 @@ from pydantic import BaseModel +class SeismicCubeSpec(BaseModel): + num_cols: int + num_rows: int + num_layers: int + x_origin: float + y_origin: float + z_origin: float + x_inc: float + y_inc: float + z_inc: float + y_flip: int + z_flip: int + rotation: float + + +class SeismicCubeBoundingBox(BaseModel): + xmin: float + ymin: float + zmin: float + xmax: float + ymax: float + zmax: float + + class SeismicCubeMeta(BaseModel): seismic_attribute: str + unit: str iso_date_or_interval: str is_observation: bool is_depth: bool - i_min: int - i_max: int - j_min: int - j_max: int - k_min: int - k_max: int - z_min: float - z_max: float - z_inc: float + bbox: SeismicCubeBoundingBox + spec: SeismicCubeSpec class VdsHandle(BaseModel): diff --git a/frontend/src/api/autogen/types.gen.ts b/frontend/src/api/autogen/types.gen.ts index aa4822f00..2a336bf49 100644 --- a/frontend/src/api/autogen/types.gen.ts +++ b/frontend/src/api/autogen/types.gen.ts @@ -544,20 +544,35 @@ export type RftWellInfo_api = { timestamps_utc_ms: Array; }; +/** + * Metadata for a seismic cube. + */ export type SeismicCubeMeta_api = { - seismic_attribute: string; - iso_date_or_interval: string; - is_observation: boolean; - is_depth: boolean; - i_min: number; - i_max: number; - j_min: number; - j_max: number; - k_min: number; - k_max: number; - z_min: number; - z_max: number; - z_inc: number; + seismicAttribute: string; + unit: string; + isoDateOrInterval: string; + isObservation: boolean; + isDepth: boolean; + bbox: BoundingBox3D_api; + spec: SeismicCubeSpec_api; +}; + +/** + * Specification for a seismic cube. + */ +export type SeismicCubeSpec_api = { + numCols: number; + numRows: number; + numLayers: number; + xOrigin: number; + yOrigin: number; + zOrigin: number; + xInc: number; + yInc: number; + zInc: number; + yFlip: number; + zFlip: number; + rotation: number; }; /** diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts index 3fd16c445..2806137a6 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceSettingsContext.ts @@ -14,16 +14,12 @@ export class ObservedSurfaceSettingsContext implements SettingsContext; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate( - this, - layerManager, - { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.ATTRIBUTE]: new AttributeSetting(), - [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - } - ); + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.ATTRIBUTE]: new AttributeSetting(), + [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + }); } getDelegate(): SettingsContextDelegate { diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts index 77d4b56d1..4d6a8e91f 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts @@ -17,19 +17,15 @@ export class RealizationGridSettingsContext implements SettingsContext; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate( - this, - layerManager, - { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.GRID_NAME]: new GridNameSetting(), - [SettingType.ATTRIBUTE]: new AttributeSetting(), - [SettingType.GRID_LAYER_K]: new GridLayerKSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - [SettingType.SHOW_GRID_LINES]: new ShowGridLinesSetting(), - } - ); + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.REALIZATION]: new RealizationSetting(), + [SettingType.GRID_NAME]: new GridNameSetting(), + [SettingType.ATTRIBUTE]: new AttributeSetting(), + [SettingType.GRID_LAYER_K]: new GridLayerKSetting(), + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + [SettingType.SHOW_GRID_LINES]: new ShowGridLinesSetting(), + }); } areCurrentSettingsValid(settings: RealizationGridSettings): boolean { diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts index fdd1ce3c6..7a6ed5dac 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsSettingsContext.ts @@ -14,10 +14,7 @@ export class RealizationPolygonsSettingsContext implements SettingsContext; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - RealizationPolygonsSettings, - keyof RealizationPolygonsSettings - >(this, layerManager, { + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), [SettingType.POLYGONS_ATTRIBUTE]: new PolygonsAttributeSetting(), diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts index 6d61463c6..d8a99a819 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts @@ -16,10 +16,7 @@ export class RealizationSurfaceSettingsContext implements SettingsContext; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - RealizationSurfaceSettings, - keyof RealizationSurfaceSettings - >(this, layerManager, { + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), [SettingType.ATTRIBUTE]: new AttributeSetting(), diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts index 9a397eaf1..7ac582c41 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer/StatisticalSurfaceSettingsContext.ts @@ -20,10 +20,7 @@ export class StatisticalSurfaceSettingsContext implements SettingsContext; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - StatisticalSurfaceSettings, - keyof StatisticalSurfaceSettings - >(this, layerManager, { + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.STATISTIC_FUNCTION]: new StatisticFunctionSetting(), [SettingType.SENSITIVITY]: new SensitivitySetting(), diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts index fc43394a5..07ef7163a 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts @@ -22,10 +22,7 @@ export class IntersectionRealizationGridSettingsContext private _contextDelegate: SettingsContextDelegate; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - IntersectionRealizationGridSettings, - keyof IntersectionRealizationGridSettings - >(this, layerManager, { + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { [SettingType.INTERSECTION]: new IntersectionSetting(), [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts index 81dd065fc..b623a5835 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts @@ -19,21 +19,17 @@ export class RealizationGridSettingsContext implements SettingsContext; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate( - this, - layerManager, - { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.GRID_NAME]: new GridNameSetting(), - [SettingType.ATTRIBUTE]: new AttributeSetting(), - [SettingType.GRID_LAYER_I_RANGE]: new GridLayerIRangeSetting(), - [SettingType.GRID_LAYER_J_RANGE]: new GridLayerJRangeSetting(), - [SettingType.GRID_LAYER_K_RANGE]: new GridLayerKRangeSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - [SettingType.SHOW_GRID_LINES]: new ShowGridLinesSetting(), - } - ); + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { + [SettingType.ENSEMBLE]: new EnsembleSetting(), + [SettingType.REALIZATION]: new RealizationSetting(), + [SettingType.GRID_NAME]: new GridNameSetting(), + [SettingType.ATTRIBUTE]: new AttributeSetting(), + [SettingType.GRID_LAYER_I_RANGE]: new GridLayerIRangeSetting(), + [SettingType.GRID_LAYER_J_RANGE]: new GridLayerJRangeSetting(), + [SettingType.GRID_LAYER_K_RANGE]: new GridLayerKRangeSetting(), + [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), + [SettingType.SHOW_GRID_LINES]: new ShowGridLinesSetting(), + }); } areCurrentSettingsValid(settings: RealizationGridSettings): boolean { diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index 10f7b6764..50e70a580 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -1,4 +1,5 @@ import { getCrosslineSliceOptions } from "@api"; +import { rotatePoint2Around } from "@lib/utils/vec2"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; @@ -10,14 +11,19 @@ import { QueryClient } from "@tanstack/react-query"; import { isEqual } from "lodash"; import { RealizationSeismicCrosslineSettingsContext } from "./RealizationSeismicCrosslineSettingsContext"; -import { RealizationSeismicCrosslineSettings } from "./types"; +import { RealizationSeismicCrosslineSettings, RealizationSeismicCrosslineStoredData } from "./types"; import { SeismicSliceData_trans, transformSeismicSlice } from "../../../settings/queries/queryDataTransforms"; export class RealizationSeismicCrosslineLayer - implements Layer + implements + Layer { - private _layerDelegate: LayerDelegate; + private _layerDelegate: LayerDelegate< + RealizationSeismicCrosslineSettings, + SeismicSliceData_trans, + RealizationSeismicCrosslineStoredData + >; private _itemDelegate: ItemDelegate; constructor(layerManager: LayerManager) { @@ -38,7 +44,7 @@ export class RealizationSeismicCrosslineLayer return this._itemDelegate; } - getLayerDelegate(): LayerDelegate { + getLayerDelegate() { return this._layerDelegate; } @@ -62,6 +68,46 @@ export class RealizationSeismicCrosslineLayer }; } + predictBoundingBox(): BoundingBox | null { + const settings = this.getSettingsContext().getDelegate().getSettings(); + const seismicCrosslineNumber = settings[SettingType.SEISMIC_CROSSLINE].getDelegate().getValue(); + const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); + const isoTimeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + const seismicCubeMeta = this._layerDelegate.getSettingsContext().getDelegate().getStoredData("seismicCubeMeta"); + + if (!seismicCubeMeta || !seismicCrosslineNumber) { + return null; + } + + const meta = seismicCubeMeta.find( + (m) => m.seismicAttribute === seismicAttribute && m.isoDateOrInterval === isoTimeOrInterval + ); + + if (!meta) { + return null; + } + + const xmin = meta.spec.xOrigin; + const xmax = meta.spec.xOrigin + meta.spec.xInc * seismicCrosslineNumber; + + const ymin = meta.spec.yOrigin; + const ymax = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * (meta.spec.numRows - 1); + + const zmin = meta.spec.zOrigin; + const zmax = meta.spec.zOrigin + meta.spec.zInc * meta.spec.zFlip * (meta.spec.numLayers - 1); + + const maxXY = { x: xmax, y: ymax }; + const minXY = { x: xmin, y: ymin }; + + const rotatedMaxXY = rotatePoint2Around(maxXY, minXY, 0); + + return { + x: [xmin, rotatedMaxXY.x], + y: [ymin, rotatedMaxXY.y], + z: [zmin, zmax], + }; + } + makeValueRange(): [number, number] | null { const data = this._layerDelegate.getData(); if (!data) { diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts index 8a3596b38..9b643571d 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts @@ -9,17 +9,20 @@ import { SeismicCrosslineSetting } from "@modules/_shared/LayerFramework/setting import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { RealizationSeismicCrosslineSettings } from "./types"; +import { RealizationSeismicCrosslineSettings, RealizationSeismicCrosslineStoredData } from "./types"; export class RealizationSeismicCrosslineSettingsContext - implements SettingsContext + implements SettingsContext { - private _contextDelegate: SettingsContextDelegate; + private _contextDelegate: SettingsContextDelegate< + RealizationSeismicCrosslineSettings, + RealizationSeismicCrosslineStoredData + >; constructor(layerManager: LayerManager) { this._contextDelegate = new SettingsContextDelegate< RealizationSeismicCrosslineSettings, - keyof RealizationSeismicCrosslineSettings + RealizationSeismicCrosslineStoredData >(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), @@ -38,7 +41,7 @@ export class RealizationSeismicCrosslineSettingsContext ); } - getDelegate(): SettingsContextDelegate { + getDelegate() { return this._contextDelegate; } @@ -49,8 +52,9 @@ export class RealizationSeismicCrosslineSettingsContext defineDependencies({ helperDependency, availableSettingsUpdater, + storedDataUpdater, queryClient, - }: DefineDependenciesArgs) { + }: DefineDependenciesArgs) { availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { const fieldIdentifier = getGlobalSetting("fieldId"); const ensembles = getGlobalSetting("ensembles"); @@ -74,6 +78,7 @@ export class RealizationSeismicCrosslineSettingsContext return [...realizations]; }); + const realizationSeismicCrosslineDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); const realization = getLocalSetting(SettingType.REALIZATION); @@ -93,6 +98,16 @@ export class RealizationSeismicCrosslineSettingsContext }); }); + storedDataUpdater("seismicCubeMeta", ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!data) { + return null; + } + + return data; + }); + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { const data = getHelperDependency(realizationSeismicCrosslineDataDep); @@ -101,7 +116,7 @@ export class RealizationSeismicCrosslineSettingsContext } const availableSeismicAttributes = [ - ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismic_attribute))), + ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismicAttribute))), ]; return availableSeismicAttributes; @@ -120,14 +135,15 @@ export class RealizationSeismicCrosslineSettingsContext ...Array.from( new Set( data - .filter((surface) => surface.seismic_attribute === seismicAttribute) - .map((el) => el.iso_date_or_interval) + .filter((surface) => surface.seismicAttribute === seismicAttribute) + .map((el) => el.isoDateOrInterval) ) ), ]; return availableTimeOrIntervals; }); + availableSettingsUpdater(SettingType.SEISMIC_CROSSLINE, ({ getLocalSetting, getHelperDependency }) => { const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); const timeOrInterval = getLocalSetting(SettingType.TIME_OR_INTERVAL); @@ -138,11 +154,11 @@ export class RealizationSeismicCrosslineSettingsContext } const seismicInfo = data.filter( (seismicInfos) => - seismicInfos.seismic_attribute === seismicAttribute && - seismicInfos.iso_date_or_interval === timeOrInterval + seismicInfos.seismicAttribute === seismicAttribute && + seismicInfos.isoDateOrInterval === timeOrInterval )[0]; - const jMin = seismicInfo.j_min; - const jMax = seismicInfo.j_max; + const jMin = 0; + const jMax = seismicInfo.spec.numRows - 1; return [jMin, jMax]; }); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts index 3b04717ef..d23a36771 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts @@ -1,3 +1,4 @@ +import { SeismicCubeMeta_api } from "@api"; import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; @@ -8,3 +9,7 @@ export type RealizationSeismicCrosslineSettings = { [SettingType.TIME_OR_INTERVAL]: string | null; [SettingType.SEISMIC_CROSSLINE]: number | null; }; + +export type RealizationSeismicCrosslineStoredData = { + seismicCubeMeta: SeismicCubeMeta_api[]; +}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts index 495169276..6b600495e 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts @@ -17,10 +17,7 @@ export class RealizationSeismicDepthSliceSettingsContext private _contextDelegate: SettingsContextDelegate; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - RealizationSeismicDepthSliceSettings, - keyof RealizationSeismicDepthSliceSettings - >(this, layerManager, { + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), [SettingType.ATTRIBUTE]: new AttributeSetting(), @@ -101,7 +98,7 @@ export class RealizationSeismicDepthSliceSettingsContext } const availableSeismicAttributes = [ - ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismic_attribute))), + ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismicAttribute))), ]; return availableSeismicAttributes; @@ -120,8 +117,8 @@ export class RealizationSeismicDepthSliceSettingsContext ...Array.from( new Set( data - .filter((surface) => surface.seismic_attribute === seismicAttribute) - .map((el) => el.iso_date_or_interval) + .filter((surface) => surface.seismicAttribute === seismicAttribute) + .map((el) => el.isoDateOrInterval) ) ), ]; @@ -138,12 +135,14 @@ export class RealizationSeismicDepthSliceSettingsContext } const seismicInfo = data.filter( (seismicInfos) => - seismicInfos.seismic_attribute === seismicAttribute && - seismicInfos.iso_date_or_interval === timeOrInterval + seismicInfos.seismicAttribute === seismicAttribute && + seismicInfos.isoDateOrInterval === timeOrInterval )[0]; - const zMin = seismicInfo.z_min; - const zMax = seismicInfo.z_max; - const zInc = seismicInfo.z_inc; + const zMin = seismicInfo.spec.zOrigin; + const zMax = + seismicInfo.spec.zOrigin + + seismicInfo.spec.zInc * seismicInfo.spec.zFlip * (seismicInfo.spec.numLayers - 1); + const zInc = seismicInfo.spec.zInc; return [zMin, zMax, zInc]; }); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts index 06e166908..e8a9296bb 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts @@ -15,10 +15,7 @@ export class RealizationSeismicInlineSettingsContext implements SettingsContext< private _contextDelegate: SettingsContextDelegate; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - RealizationSeismicInlineSettings, - keyof RealizationSeismicInlineSettings - >(this, layerManager, { + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), [SettingType.ATTRIBUTE]: new AttributeSetting(), @@ -99,7 +96,7 @@ export class RealizationSeismicInlineSettingsContext implements SettingsContext< } const availableSeismicAttributes = [ - ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismic_attribute))), + ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismicAttribute))), ]; return availableSeismicAttributes; @@ -118,8 +115,8 @@ export class RealizationSeismicInlineSettingsContext implements SettingsContext< ...Array.from( new Set( data - .filter((surface) => surface.seismic_attribute === seismicAttribute) - .map((el) => el.iso_date_or_interval) + .filter((surface) => surface.seismicAttribute === seismicAttribute) + .map((el) => el.isoDateOrInterval) ) ), ]; @@ -136,11 +133,11 @@ export class RealizationSeismicInlineSettingsContext implements SettingsContext< } const seismicInfo = data.filter( (seismicInfos) => - seismicInfos.seismic_attribute === seismicAttribute && - seismicInfos.iso_date_or_interval === timeOrInterval + seismicInfos.seismicAttribute === seismicAttribute && + seismicInfos.isoDateOrInterval === timeOrInterval )[0]; - const iMin = seismicInfo.i_min; - const iMax = seismicInfo.i_max; + const iMin = 0; + const iMax = seismicInfo.spec.numCols - 1; return [iMin, iMax]; }); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts index 6d61463c6..d8a99a819 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts @@ -16,10 +16,7 @@ export class RealizationSurfaceSettingsContext implements SettingsContext; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - RealizationSurfaceSettings, - keyof RealizationSurfaceSettings - >(this, layerManager, { + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), [SettingType.ATTRIBUTE]: new AttributeSetting(), diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts index 201eb9174..6c30252fc 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts @@ -76,6 +76,7 @@ function buildVtkStylePolyDataFromFenceSections(fenceSections: FenceMeshSection_ export function makeIntersectionLayer({ id, + name, data, colorScale, settings, @@ -84,6 +85,7 @@ export function makeIntersectionLayer({ const grid3dIntersectionLayer = new Grid3DLayer({ id, + name, pointsData: polyData.points, polysData: polyData.polys, propertiesData: polyData.props, @@ -99,7 +101,8 @@ export function makeIntersectionLayer({ ZIncreasingDownwards: false, gridLines: settings.showGridLines, material: { ambient: 0.4, diffuse: 0.7, shininess: 8, specularColor: [25, 25, 25] }, - pickable: false, + pickable: true, }); + return grid3dIntersectionLayer; } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index 64ddf2c5f..fe76ea258 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -70,8 +70,16 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { data, colorScale, settings, + isLoading, + predictedNextBoundingBox, }: VisualizationFunctionArgs): Layer { - const bbox = data.bbox_utm; + let bbox = data.bbox_utm; + if (isLoading && predictedNextBoundingBox) { + bbox = [ + [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], predictedNextBoundingBox.z[0]], + [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], predictedNextBoundingBox.z[1]], + ]; + } const properties = data.dataFloat32Arr; let startPosition: [number, number, number] = [0, 0, 0]; @@ -126,6 +134,7 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), boundingBox, zIncreaseDownwards: true, + isLoading, }), }); }; diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index b0f9ddd05..85ff7ecb9 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -5,7 +5,6 @@ import { ViewContext } from "@framework/ModuleContext"; import { useViewStatusWriter } from "@framework/StatusWriter"; import { WorkbenchSession } from "@framework/WorkbenchSession"; import { WorkbenchSettings } from "@framework/WorkbenchSettings"; -import { PendingWrapper } from "@lib/components/PendingWrapper"; import { useElementSize } from "@lib/hooks/useElementSize"; import { Rect3D, outerRectContainsInnerRect } from "@lib/utils/geometry"; import { RealizationSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; @@ -95,8 +94,6 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { let numCols = 0; let numRows = 0; - let numLoadingLayers = 0; - const viewsAndLayers = VISUALIZATION_FACTORY.make(props.layerManager); numCols = Math.ceil(Math.sqrt(viewsAndLayers.views.length)); @@ -180,7 +177,6 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { } } - numLoadingLayers = viewsAndLayers.numLoadingLayers; statusWriter.setLoading(viewsAndLayers.numLoadingLayers > 0); for (const message of viewsAndLayers.errorMessages) { @@ -205,18 +201,16 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { return (
- 0}> -
- -
-
+
+ +
); } diff --git a/frontend/src/modules/Intersection/settings/components/layerSettings/seismicLayer.tsx b/frontend/src/modules/Intersection/settings/components/layerSettings/seismicLayer.tsx index 98ecdb100..6c50649bf 100644 --- a/frontend/src/modules/Intersection/settings/components/layerSettings/seismicLayer.tsx +++ b/frontend/src/modules/Intersection/settings/components/layerSettings/seismicLayer.tsx @@ -90,15 +90,15 @@ export function SeismicLayerSettingsComponent(props: SeismicLayerSettingsProps): seismicCubeMetaListQuery.data .filter((el) => { return ( - el.is_depth && - el.is_observation === (newSettings.dataType === SeismicDataType.OBSERVED) && + el.isDepth && + el.isObservation === (newSettings.dataType === SeismicDataType.OBSERVED) && ((newSettings.surveyType === SeismicSurveyType.THREE_D && - !isIsoStringInterval(el.iso_date_or_interval)) || + !isIsoStringInterval(el.isoDateOrInterval)) || (newSettings.surveyType === SeismicSurveyType.FOUR_D && - isIsoStringInterval(el.iso_date_or_interval))) + isIsoStringInterval(el.isoDateOrInterval))) ); }) - .map((el) => el.seismic_attribute) + .map((el) => el.seismicAttribute) ) ) ); @@ -109,16 +109,16 @@ export function SeismicLayerSettingsComponent(props: SeismicLayerSettingsProps): seismicCubeMetaListQuery.data .filter((el) => { return ( - el.is_depth && - el.seismic_attribute === newSettings.attribute && - el.is_observation === (newSettings.dataType === SeismicDataType.OBSERVED) && + el.isDepth && + el.seismicAttribute === newSettings.attribute && + el.isObservation === (newSettings.dataType === SeismicDataType.OBSERVED) && ((newSettings.surveyType === SeismicSurveyType.THREE_D && - !isIsoStringInterval(el.iso_date_or_interval)) || + !isIsoStringInterval(el.isoDateOrInterval)) || (newSettings.surveyType === SeismicSurveyType.FOUR_D && - isIsoStringInterval(el.iso_date_or_interval))) + isIsoStringInterval(el.isoDateOrInterval))) ); }) - .map((el) => el.iso_date_or_interval) + .map((el) => el.isoDateOrInterval) ) ).sort() ); diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts index f7adfdd34..b4f31ca37 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts @@ -9,7 +9,15 @@ import { UnsubscribeHandlerDelegate } from "./UnsubscribeHandlerDelegate"; import { PublishSubscribe, PublishSubscribeDelegate } from "../../utils/PublishSubscribeDelegate"; import { LayerManager, LayerManagerTopic } from "../framework/LayerManager/LayerManager"; import { SharedSetting } from "../framework/SharedSetting/SharedSetting"; -import { BoundingBox, Layer, SerializedLayer, SerializedType, Settings, SettingsContext } from "../interfaces"; +import { + BoundingBox, + Layer, + SerializedLayer, + SerializedType, + Settings, + SettingsContext, + StoredData, +} from "../interfaces"; export enum LayerDelegateTopic { STATUS = "STATUS", @@ -41,11 +49,11 @@ export type LayerDelegatePayloads = { * It is responsible for (re-)fetching the data whenever changes to settings make it necessary. * It also manages the status of the layer (loading, success, error). */ -export class LayerDelegate +export class LayerDelegate> implements PublishSubscribe> { - private _owner: Layer; - private _settingsContext: SettingsContext; + private _owner: Layer; + private _settingsContext: SettingsContext; private _layerManager: LayerManager; private _unsubscribeHandler: UnsubscribeHandlerDelegate = new UnsubscribeHandlerDelegate(); private _cancellationPending: boolean = false; @@ -54,15 +62,17 @@ export class LayerDelegate private _status: LayerStatus = LayerStatus.IDLE; private _data: TData | null = null; private _error: StatusMessage | string | null = null; + private _prevBoundingBox: BoundingBox | null = null; + private _predictedBoundingBox: BoundingBox | null = null; private _boundingBox: BoundingBox | null = null; private _valueRange: [number, number] | null = null; private _coloringType: LayerColoringType; private _isSubordinated: boolean = false; constructor( - owner: Layer, + owner: Layer, layerManager: LayerManager, - settingsContext: SettingsContext, + settingsContext: SettingsContext, coloringType: LayerColoringType ) { this._owner = owner; @@ -116,7 +126,7 @@ export class LayerDelegate return this._data; } - getSettingsContext(): SettingsContext { + getSettingsContext(): SettingsContext { return this._settingsContext; } @@ -124,6 +134,17 @@ export class LayerDelegate return this._boundingBox; } + getLastValidBoundingBox(): BoundingBox | null { + if (this._boundingBox) { + return this._boundingBox; + } + return this._prevBoundingBox; + } + + getPredictedBoundingBox(): BoundingBox | null { + return this._predictedBoundingBox; + } + getColoringType(): LayerColoringType { return this._coloringType; } @@ -227,9 +248,11 @@ export class LayerDelegate return; } - this.setStatus(LayerStatus.LOADING); this.invalidateBoundingBox(); this.invalidateValueRange(); + this._predictedBoundingBox = this._owner.predictBoundingBox?.() ?? null; + + this.setStatus(LayerStatus.LOADING); try { this._data = await this._owner.fetchData(queryClient); @@ -297,6 +320,15 @@ export class LayerDelegate } private invalidateBoundingBox(): void { + if (this._boundingBox) { + this._prevBoundingBox = { + x: [this._boundingBox.x[0], this._boundingBox.x[1]], + y: [this._boundingBox.y[0], this._boundingBox.y[1]], + z: [this._boundingBox.z[0], this._boundingBox.z[1]], + }; + } else { + this._prevBoundingBox = null; + } this._boundingBox = null; } diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts index 1865aded7..62f5158a1 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts @@ -7,10 +7,12 @@ import { GlobalSettings, LayerManager, LayerManagerTopic } from "../framework/La import { AvailableValuesType, EachAvailableValuesType, + NullableStoredData, SerializedSettingsState, Setting, Settings, SettingsContext, + StoredData, UpdateFunc, } from "../interfaces"; @@ -49,19 +51,24 @@ export type SettingsContextDelegateState - implements PublishSubscribe +export class SettingsContextDelegate< + TSettings extends Settings, + TStoredData extends StoredData = Record, + TKey extends keyof TSettings = keyof TSettings, + TStoredDataKey extends keyof TStoredData = keyof TStoredData +> implements PublishSubscribe { - private _parentContext: SettingsContext; + private _parentContext: SettingsContext; private _layerManager: LayerManager; private _settings: { [K in TKey]: Setting } = {} as { [K in TKey]: Setting }; private _overriddenSettings: { [K in TKey]: TSettings[K] } = {} as { [K in TKey]: TSettings[K] }; private _publishSubscribeDelegate = new PublishSubscribeDelegate(); private _unsubscribeHandler: UnsubscribeHandlerDelegate = new UnsubscribeHandlerDelegate(); private _loadingState: SettingsContextLoadingState = SettingsContextLoadingState.LOADING; + private _storedData: NullableStoredData = {} as NullableStoredData; constructor( - context: SettingsContext, + context: SettingsContext, layerManager: LayerManager, settings: { [K in TKey]: Setting } ) { @@ -197,10 +204,18 @@ export class SettingsContextDelegate(key: K, data: TStoredData[K] | null): void { + this._storedData[key] = data; + } + getSettings() { return this._settings; } + getStoredData(key: TStoredDataKey): TStoredData[TStoredDataKey] | null { + return this._storedData[key]; + } + makeSnapshotGetter(topic: T): () => SettingsContextDelegatePayloads[T] { const snapshotGetter = (): any => { if (topic === SettingsContextDelegateTopic.SETTINGS_CHANGED) { @@ -287,7 +302,7 @@ export class SettingsContextDelegate, TSettings, K> ): Dependency, TSettings, K> => { const dependency = new Dependency, TSettings, K>( - this as unknown as SettingsContextDelegate, + this as unknown as SettingsContextDelegate, updateFunc, makeSettingGetter, makeGlobalSettingGetter @@ -314,6 +329,26 @@ export class SettingsContextDelegate( + key: K, + updateFunc: UpdateFunc[K], TSettings, TKey> + ): Dependency[K], TSettings, TKey> => { + const dependency = new Dependency[K], TSettings, TKey>( + this as unknown as SettingsContextDelegate, + updateFunc, + makeSettingGetter, + makeGlobalSettingGetter + ); + + dependency.subscribe((storedData: TStoredData[K] | null) => { + this.setStoredData(key, storedData); + }); + + dependency.initialize(); + + return dependency; + }; + const helperDependency = ( update: (args: { getLocalSetting: (settingName: T) => TSettings[T]; @@ -323,7 +358,7 @@ export class SettingsContextDelegate T ) => { const dependency = new Dependency( - this as unknown as SettingsContextDelegate, + this as unknown as SettingsContextDelegate, update, makeSettingGetter, makeGlobalSettingGetter @@ -337,6 +372,7 @@ export class SettingsContextDelegate | null) => void> = new Set(); private _loadingDependencies: Set<(loading: boolean, hasDependencies: boolean) => void> = new Set(); - private _contextDelegate: SettingsContextDelegate; + private _contextDelegate: SettingsContextDelegate; private _makeSettingGetter: (key: K, handler: (value: TSettings[K]) => void) => void; private _makeGlobalSettingGetter: ( @@ -39,7 +39,7 @@ export class Dependency, + contextDelegate: SettingsContextDelegate, updateFunc: UpdateFunc, makeSettingGetter: (key: K, handler: (value: TSettings[K]) => void) => void, makeGlobalSettingGetter: ( diff --git a/frontend/src/modules/_shared/LayerFramework/interfaces.ts b/frontend/src/modules/_shared/LayerFramework/interfaces.ts index 8a492e11b..ca834ff66 100644 --- a/frontend/src/modules/_shared/LayerFramework/interfaces.ts +++ b/frontend/src/modules/_shared/LayerFramework/interfaces.ts @@ -109,19 +109,21 @@ export interface FetchDataFunction; } -export interface Layer extends Item { - getLayerDelegate(): LayerDelegate; +export interface Layer> + extends Item { + getLayerDelegate(): LayerDelegate; doSettingsChangesRequireDataRefetch(prevSettings: TSettings, newSettings: TSettings): boolean; fetchData(queryClient: QueryClient): Promise; makeBoundingBox?(): BoundingBox | null; + predictBoundingBox?(): BoundingBox | null; makeValueRange?(): [number, number] | null; } -export function instanceofLayer(item: Item): item is Layer { +export function instanceofLayer(item: Item): item is Layer { return ( - (item as Layer).getItemDelegate !== undefined && - (item as Layer).doSettingsChangesRequireDataRefetch !== undefined && - (item as Layer).fetchData !== undefined + (item as Layer).getItemDelegate !== undefined && + (item as Layer).doSettingsChangesRequireDataRefetch !== undefined && + (item as Layer).fetchData !== undefined ); } @@ -138,11 +140,20 @@ export interface UpdateFunc { +export interface DefineDependenciesArgs< + TSettings extends Settings, + TStoredData extends StoredData = Record, + TKey extends keyof TSettings = keyof TSettings, + TStoredDataKey extends keyof TStoredData = keyof TStoredData +> { availableSettingsUpdater: ( settingName: TKey, update: UpdateFunc, TSettings, TKey> ) => Dependency, TSettings, TKey>; + storedDataUpdater: ( + key: TStoredDataKey, + update: UpdateFunc[TStoredDataKey], TSettings, TKey> + ) => Dependency[TStoredDataKey], TSettings, TKey>; helperDependency: ( update: (args: { getLocalSetting: (settingName: T) => TSettings[T]; @@ -156,10 +167,15 @@ export interface DefineDependenciesArgs { - getDelegate(): SettingsContextDelegate; +export interface SettingsContext< + TSettings extends Settings, + TStoredData extends StoredData = Record, + TKey extends keyof TSettings = keyof TSettings, + TStoredDataKey extends keyof TStoredData = keyof TStoredData +> { + getDelegate(): SettingsContextDelegate; areCurrentSettingsValid?: (settings: TSettings) => boolean; - defineDependencies(args: DefineDependenciesArgs): void; + defineDependencies(args: DefineDependenciesArgs): void; } // Required when making "AvailableValuesType" for all settings in an object ("TSettings") @@ -203,3 +219,8 @@ export interface Setting { } export type Settings = { [key in SettingType]?: any }; + +export type StoredData = Record; +export type NullableStoredData = { + [key in keyof TStoredData]: TStoredData[key] | null; +}; diff --git a/frontend/src/modules/_shared/LayerFramework/layers/LayerComponent.tsx b/frontend/src/modules/_shared/LayerFramework/layers/LayerComponent.tsx index e88868c63..a4a5aee3f 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/LayerComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/layers/LayerComponent.tsx @@ -18,7 +18,7 @@ import { Layer, Setting } from "../interfaces"; import { SettingComponent } from "../settings/SettingComponent"; export type LayerComponentProps = { - layer: Layer; + layer: Layer; }; export function LayerComponent(props: LayerComponentProps): React.ReactNode { diff --git a/frontend/src/modules/_shared/LayerFramework/layers/LayerRegistry.ts b/frontend/src/modules/_shared/LayerFramework/layers/LayerRegistry.ts index c27ed29b7..96d8459cd 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/LayerRegistry.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/LayerRegistry.ts @@ -2,13 +2,14 @@ import { LayerManager } from "../framework/LayerManager/LayerManager"; import { Layer } from "../interfaces"; export class LayerRegistry { - private static _registeredLayers: Map }> = new Map(); + private static _registeredLayers: Map }> = + new Map(); - static registerLayer(ctor: { new (layerManager: LayerManager): Layer }): void { + static registerLayer(ctor: { new (layerManager: LayerManager): Layer }): void { this._registeredLayers.set(ctor.name, ctor); } - static makeLayer(layerName: string, layerManager: LayerManager): Layer { + static makeLayer(layerName: string, layerManager: LayerManager): Layer { const Layer = this._registeredLayers.get(layerName); if (!Layer) { throw new Error(`Layer ${layerName} not found`); diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesSettingsContext.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesSettingsContext.ts index 49f531a91..fcaf53612 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesSettingsContext.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesSettingsContext.ts @@ -12,10 +12,7 @@ export class DrilledWellTrajectoriesSettingsContext implements SettingsContext; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - DrilledWellTrajectoriesSettings, - keyof DrilledWellTrajectoriesSettings - >(this, layerManager, { + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.SMDA_WELLBORE_HEADERS]: new DrilledWellboresSetting(), }); diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksSettingsContext.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksSettingsContext.ts index c48200cf9..aabf40574 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksSettingsContext.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksSettingsContext.ts @@ -13,10 +13,7 @@ export class DrilledWellborePicksSettingsContext implements SettingsContext; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - DrilledWellborePicksSettings, - keyof DrilledWellborePicksSettings - >(this, layerManager, { + this._contextDelegate = new SettingsContextDelegate(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.SMDA_WELLBORE_HEADERS]: new DrilledWellboresSetting(), [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts index 21d84cfe2..19dd7c6d4 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts @@ -26,6 +26,8 @@ export type VisualizationFunctionArgs = { data: TData; colorScale: ColorScaleWithName; settings: TSettings; + isLoading: boolean; + predictedNextBoundingBox: BoundingBox | null; }; export type TargetReturnTypes = { @@ -63,7 +65,7 @@ export class VisualizationFactory { private _visualizationFunctions: Map> = new Map(); registerVisualizationFunction( - layerCtor: { new (layerManager: LayerManager): Layer }, + layerCtor: { new (layerManager: LayerManager): Layer }, func: MakeVisualizationFunction ): void { if (this._visualizationFunctions.has(layerCtor.name)) { @@ -128,16 +130,18 @@ export class VisualizationFactory { collectedNumLoadingLayers++; } - if (child.getLayerDelegate().getStatus() !== LayerStatus.SUCCESS) { - if (child.getLayerDelegate().getStatus() === LayerStatus.ERROR) { - const error = child.getLayerDelegate().getError(); - if (error) { - collectedErrorMessages.push(error); - } + if (child.getLayerDelegate().getStatus() === LayerStatus.ERROR) { + const error = child.getLayerDelegate().getError(); + if (error) { + collectedErrorMessages.push(error); } continue; } + if (child.getLayerDelegate().getData() === null) { + continue; + } + const colorScale = this.findColorScale(child); const layer = this.makeLayer(child, colorScale?.colorScale ?? undefined); @@ -188,6 +192,8 @@ export class VisualizationFactory { data: layer.getLayerDelegate().getData(), colorScale, settings: layer.getLayerDelegate().getSettingsContext().getDelegate().getValues(), + isLoading: layer.getLayerDelegate().getStatus() === LayerStatus.LOADING, + predictedNextBoundingBox: layer.getLayerDelegate().getPredictedBoundingBox(), }); } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer.ts index 219a9898a..fb204ff49 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer.ts @@ -1,6 +1,7 @@ import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; export class ExtendedSimpleMeshLayer extends SimpleMeshLayer { + static name = "ExtendedSimpleMeshLayer"; getShaders() { return { ...super.getShaders(), diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts b/frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts index a96129875..6e08e97a7 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts @@ -2,19 +2,20 @@ import { CompositeLayer, GetPickingInfoParams, Layer, LayersList, PickingInfo } import { DragDirection } from "./DragHandleLayer"; -export type MovableLayerWrapperProps, TProps extends {}> = { +export type MovableLayerWrapperProps, TProps extends Record> = { wrappedLayer: TLayer; draggable?: boolean; dragDirection?: DragDirection; -} & TProps; +}; export type MovableLayerPickingInfo = { isMovable: boolean; } & PickingInfo; -export class MovableLayerWrapper, TProps extends {}> extends CompositeLayer< - MovableLayerWrapperProps -> { +export class MovableLayerWrapper< + TLayer extends Layer, + TProps extends Record +> extends CompositeLayer> { static layerName: string = "MovableLayerWrapper"; getPickingInfo({ info }: GetPickingInfoParams): MovableLayerPickingInfo { diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts index ec4519a23..95a378ddd 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts @@ -7,6 +7,7 @@ import { UpdateParameters, } from "@deck.gl/core"; import { PathLayer } from "@deck.gl/layers"; +import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; import { Geometry } from "@luma.gl/engine"; import { ExtendedSimpleMeshLayer } from "./ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer"; @@ -27,6 +28,7 @@ export type SeismicFenceMeshLayerProps = { colorMapFunction: (value: number) => [number, number, number]; hoverable?: boolean; zIncreaseDownwards?: boolean; + isLoading?: boolean; }; export class SeismicFenceMeshLayer extends CompositeLayer { @@ -114,20 +116,36 @@ export class SeismicFenceMeshLayer extends CompositeLayer[] = [ - new ExtendedSimpleMeshLayer({ - id: "seismic-fence-mesh-layer", - data: [0], - mesh: geometry, - getPosition: startPosition, - getColor: [255, 255, 255, 255], - material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, - pickable: true, - }), - ]; + const layers: Layer[] = []; + + if (isLoading) { + layers.push( + new SimpleMeshLayer({ + id: "seismic-fence-mesh-layer-loading", + data: [0], + mesh: geometry, + getColor: [255, 255, 255, 255], + pickable: false, + getPosition: startPosition, + material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, + }) + ); + } else { + layers.push( + new ExtendedSimpleMeshLayer({ + id: "seismic-fence-mesh-layer", + data: [0], + mesh: geometry, + getPosition: startPosition, + getColor: [255, 255, 255, 255], + material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, + pickable: true, + }) + ); + } if (isHovered && hoverable) { layers.push( From 2d188057b365bd5bc4f067617fac0006b760f99b Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 11 Feb 2025 17:29:07 +0100 Subject: [PATCH 47/97] wip --- .../RealizationSeismicCrosslineLayer.ts | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index 50e70a580..a4eb10094 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -61,6 +61,12 @@ export class RealizationSeismicCrosslineLayer return null; } + console.debug("real", { + x: [data.bbox_utm[0][0], data.bbox_utm[1][0]], + y: [data.bbox_utm[0][1], data.bbox_utm[1][1]], + z: [data.u_min, data.u_max], + }); + return { x: [data.bbox_utm[0][0], data.bbox_utm[1][0]], y: [data.bbox_utm[0][1], data.bbox_utm[1][1]], @@ -88,24 +94,28 @@ export class RealizationSeismicCrosslineLayer } const xmin = meta.spec.xOrigin; - const xmax = meta.spec.xOrigin + meta.spec.xInc * seismicCrosslineNumber; + const xmax = meta.spec.xOrigin + meta.spec.xInc * (meta.spec.numCols - 1); - const ymin = meta.spec.yOrigin; - const ymax = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * (meta.spec.numRows - 1); + const ymin = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * seismicCrosslineNumber; + const ymax = ymin; const zmin = meta.spec.zOrigin; const zmax = meta.spec.zOrigin + meta.spec.zInc * meta.spec.zFlip * (meta.spec.numLayers - 1); const maxXY = { x: xmax, y: ymax }; const minXY = { x: xmin, y: ymin }; + const origin = { x: meta.spec.xOrigin, y: meta.spec.yOrigin }; - const rotatedMaxXY = rotatePoint2Around(maxXY, minXY, 0); + const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotation / 180.0) * Math.PI); + const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotation / 180.0) * Math.PI); - return { - x: [xmin, rotatedMaxXY.x], - y: [ymin, rotatedMaxXY.y], + console.debug("predicted", { + x: [rotatedMinXY.x, rotatedMaxXY.x], + y: [rotatedMinXY.y, rotatedMaxXY.y], z: [zmin, zmax], - }; + }); + + return { x: [rotatedMinXY.x, rotatedMaxXY.x], y: [rotatedMinXY.y, rotatedMaxXY.y], z: [zmin, zmax] }; } makeValueRange(): [number, number] | null { From 378f1ebcc44fd1d47653202bc05b105f0717f0e7 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 11 Feb 2025 23:04:23 +0100 Subject: [PATCH 48/97] wip --- .../visualization/makeSeismicFenceMeshLayer.ts | 12 +++++++++++- .../implementations/SeismicCrosslineSetting.tsx | 2 +- .../customDeckGlLayers/SeismicFenceMeshLayer.ts | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index fe76ea258..0461fab91 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -74,11 +74,17 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { predictedNextBoundingBox, }: VisualizationFunctionArgs): Layer { let bbox = data.bbox_utm; + let adjustedData = data; if (isLoading && predictedNextBoundingBox) { bbox = [ [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], predictedNextBoundingBox.z[0]], [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], predictedNextBoundingBox.z[1]], ]; + adjustedData = { + ...data, + u_num_samples: 2, + v_num_samples: 2, + }; } const properties = data.dataFloat32Arr; @@ -119,7 +125,11 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { }; } - const { vertices, indices } = generatePointFenceMesh(data.u_num_samples, data.v_num_samples, transformUVToXYZ); + const { vertices, indices } = generatePointFenceMesh( + adjustedData.u_num_samples, + adjustedData.v_num_samples, + transformUVToXYZ + ); return new MovableLayerWrapper({ wrappedLayer: new SeismicFenceMeshLayer({ diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx index 11aaa15ff..5bac36a98 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx @@ -88,7 +88,7 @@ export class SeismicCrosslineSetting implements Setting { max={props.availableValues[1] ?? 1} onChange={handleSliderChange} value={props.value ?? props.availableValues[0] ?? 1} - debounceTimeMs={500} + debounceTimeMs={0} valueLabelDisplay="auto" />
diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts index 95a378ddd..538db6696 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts @@ -127,7 +127,7 @@ export class SeismicFenceMeshLayer extends CompositeLayer Date: Wed, 12 Feb 2025 13:02:32 +0100 Subject: [PATCH 49/97] wip --- frontend/package-lock.json | 13 ++- frontend/package.json | 4 +- .../RealizationSeismicCrosslineLayer.ts | 6 - .../makeSeismicFenceMeshLayer.ts | 2 +- .../SeismicFenceMeshLayer.ts | 106 +++++++++++++++++- .../_private}/ExtendedSimpleMeshLayer.ts | 0 .../SeismicFenceMeshLayer/_private/worker.ts | 42 +++++++ frontend/tsconfig.json | 3 +- 8 files changed, 161 insertions(+), 15 deletions(-) rename frontend/src/modules/_shared/customDeckGlLayers/{ => SeismicFenceMeshLayer}/SeismicFenceMeshLayer.ts (59%) rename frontend/src/modules/_shared/customDeckGlLayers/{ExtendedSimpleMeshLayer => SeismicFenceMeshLayer/_private}/ExtendedSimpleMeshLayer.ts (100%) create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/_private/worker.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8c1e71e7f..4e67ab471 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -45,7 +45,8 @@ "react-plotly.js": "^2.6.0", "simplify-js": "^1.2.4", "uuid": "^9.0.0", - "wonka": "^6.3.4" + "wonka": "^6.3.4", + "workerpool": "^9.2.0" }, "devDependencies": { "@hey-api/openapi-ts": "^0.61.1", @@ -59,6 +60,7 @@ "@types/react-dom": "^18.2.7", "@types/react-plotly.js": "^2.6.0", "@types/uuid": "^9.0.0", + "@types/workerpool": "^6.4.7", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react": "^4.2.1", @@ -6291,6 +6293,15 @@ "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", "dev": true }, + "node_modules/@types/workerpool": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@types/workerpool/-/workerpool-6.4.7.tgz", + "integrity": "sha512-DI2U4obcMzFViyNjLw0xXspim++qkAJ4BWRdYPVMMFtOpTvMr6PAk3UTZEoSqnZnvgUkJ3ck97Ybk+iIfuJHMg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 35c86fe7e..e7c614f7f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -56,7 +56,8 @@ "react-plotly.js": "^2.6.0", "simplify-js": "^1.2.4", "uuid": "^9.0.0", - "wonka": "^6.3.4" + "wonka": "^6.3.4", + "workerpool": "^9.2.0" }, "devDependencies": { "@hey-api/openapi-ts": "^0.61.1", @@ -70,6 +71,7 @@ "@types/react-dom": "^18.2.7", "@types/react-plotly.js": "^2.6.0", "@types/uuid": "^9.0.0", + "@types/workerpool": "^6.4.7", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react": "^4.2.1", diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index a4eb10094..27742f196 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -109,12 +109,6 @@ export class RealizationSeismicCrosslineLayer const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotation / 180.0) * Math.PI); const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotation / 180.0) * Math.PI); - console.debug("predicted", { - x: [rotatedMinXY.x, rotatedMaxXY.x], - y: [rotatedMinXY.y, rotatedMaxXY.y], - z: [zmin, zmax], - }); - return { x: [rotatedMinXY.x, rotatedMaxXY.x], y: [rotatedMinXY.y, rotatedMaxXY.y], z: [zmin, zmax] }; } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index 0461fab91..2f61bce7e 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -3,7 +3,7 @@ import { SeismicSliceData_trans } from "@modules/3DViewerNew/settings/queries/qu import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; import { MovableLayerWrapper } from "@modules/_shared/customDeckGlLayers/MovableLayerWrapper"; -import { SeismicFenceMeshLayer } from "@modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer"; +import { SeismicFenceMeshLayer } from "@modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer"; /* * Generate a mesh for a seismic fence plot diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts similarity index 59% rename from frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts rename to frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts index 538db6696..7de4fe564 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts @@ -10,21 +10,28 @@ import { PathLayer } from "@deck.gl/layers"; import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; import { Geometry } from "@luma.gl/engine"; -import { ExtendedSimpleMeshLayer } from "./ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer"; +import workerpool from "workerpool"; + +import { ExtendedSimpleMeshLayer } from "./_private/ExtendedSimpleMeshLayer"; +import { Space, WebworkerParameters, makeMesh } from "./_private/worker"; export type SeismicFenceMeshLayerPickingInfo = { properties?: { name: string; value: number }[]; } & PickingInfo; +export type SeismicFenceSection = { + numSamplesU: number; + numSamplesV: number; + properties: Float32Array; + boundingBox: number[][]; // [minX, minY, minZ, maxX, maxY, maxZ] +}; + export type SeismicFenceMeshLayerProps = { name?: string; startPosition: [number, number, number]; data: { - vertices: Float32Array; - indices: Uint32Array; - properties: Float32Array; + sections: SeismicFenceSection[]; }; - boundingBox: number[][]; // [minX, minY, minZ, maxX, maxY, maxZ] colorMapFunction: (value: number) => [number, number, number]; hoverable?: boolean; zIncreaseDownwards?: boolean; @@ -34,12 +41,101 @@ export type SeismicFenceMeshLayerProps = { export class SeismicFenceMeshLayer extends CompositeLayer { static layerName: string = "SeismicFenceMeshLayer"; + private _pool = workerpool.pool({ + workerType: "web", + maxWorkers: 10, + workerOpts: { + // By default, Vite uses a module worker in dev mode, which can cause your application to fail. Therefore, we need to use a module worker in dev mode and a classic worker in prod mode. + type: import.meta.env.PROD ? undefined : "module", + }, + }); + private _numTasks = 0; + private _numTasksCompleted = 0; + private _numTasksFailed = 0; + private _transVertices: Float32Array | null = null; + private _transIndices: Uint32Array | null = null; + // @ts-expect-error - private state!: { geometry: Geometry; isHovered: boolean; }; + private calcNumVerticesForSection(section: SeismicFenceSection): number { + return section.numSamplesU * section.numSamplesV * 3; + } + + private calcNumIndicesForSection(section: SeismicFenceSection): number { + return (section.numSamplesU - 1) * (section.numSamplesV - 1) * 6; + } + + private initTransferableObjects() { + const { data } = this.props; + + for (const section of data.sections) { + const numVertices = this.calcNumVerticesForSection(section); + const numIndices = this.calcNumIndicesForSection(section); + + this._transVertices = new Float32Array(numVertices); + this._transIndices = new Uint32Array(numIndices); + } + } + + private checkIfAllTasksCompleted() { + if (this._numTasks === this._numTasksCompleted) { + this.setState({ + geometry: new Geometry({ + attributes: { + positions: this._transVertices, + colors: { + value: this.makeColorsArray(), + size: 4, + }, + }, + topology: "triangle-list", + indices: this._transIndices, + }), + }); + } + } + + private rebuildMesh() { + const params: WebworkerParameters[] = []; + + this.initTransferableObjects(); + + let verticesIndex = 0; + let indicesIndex = 0; + for (const section of this.props.data.sections) { + this._numTasks++; + + const params: WebworkerParameters = { + numSamplesU: section.numSamplesU, + numSamplesV: section.numSamplesV, + boundingBox: section.boundingBox, + startVerticesIndex: verticesIndex, + startIndicesIndex: indicesIndex, + transVertices: this._transVertices, + transIndices: this._transIndices, + space: Space.FENCE, + }; + + verticesIndex += this.calcNumVerticesForSection(section); + indicesIndex += this.calcNumIndicesForSection(section); + + this._pool + .exec(makeMesh, [{ ...params }]) + .then(() => { + this; + this._numTasksCompleted++; + this.checkIfAllTasksCompleted(); + }) + .catch(() => { + this._numTasksFailed++; + }); + } + } + private makeColorsArray(): Float32Array { const { data, colorMapFunction } = this.props; diff --git a/frontend/src/modules/_shared/customDeckGlLayers/ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/_private/ExtendedSimpleMeshLayer.ts similarity index 100% rename from frontend/src/modules/_shared/customDeckGlLayers/ExtendedSimpleMeshLayer/ExtendedSimpleMeshLayer.ts rename to frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/_private/ExtendedSimpleMeshLayer.ts diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/_private/worker.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/_private/worker.ts new file mode 100644 index 000000000..6562dfb9d --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/_private/worker.ts @@ -0,0 +1,42 @@ +import workerpool from "workerpool"; + +export enum Space { + FENCE = "FENCE", + LAYER = "LAYER", +} + +export type WebworkerParameters = { + transVertices: Float32Array; + transIndices: Uint32Array; + startVerticesIndex: number; + startIndicesIndex: number; + numSamplesU: number; + numSamplesV: number; + boundingBox: number[][]; + space: Space; +}; + +export type WebworkerResult = { + vertices: Float32Array; + indices: Uint32Array; + outlineIndices: Uint32Array; +}; + +export function makeMesh(parameters: WebworkerParameters): WebworkerResult { + let transformUVToXYZ: (u: number, v: number) => [number, number, number] = () => { + throw new Error("transformUVToXYZ not implemented"); + }; + + if (parameters.space === Space.FENCE) { + transformUVToXYZ = (u: number, v: number): [number, number, number] => { + const x = v * (bbox[1][0] - bbox[0][0]); + const y = v * (bbox[1][1] - bbox[0][1]); + const z = -(u * (data.u_max - data.u_min)); + return [x, y, z]; + }; + } +} + +workerpool.worker({ + makeMesh, +}); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 24dd3b8cc..a239ef601 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -14,7 +14,8 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx" + "jsx": "react-jsx", + "types": ["vite/client"] }, "include": ["src", "tests"], "exclude": ["tests/ct/playwright/", "**/_playwright.config.ts"], From 84e3b77d47137fbda30bc37939c914e36a66dde6 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 12 Feb 2025 18:16:18 +0100 Subject: [PATCH 50/97] wip --- frontend/index.html | 2 +- frontend/src/assets/textures/checkboard.bmp | Bin 0 -> 40056 bytes frontend/src/assets/textures/checkboard.png | Bin 0 -> 387 bytes .../RealizationSeismicCrosslineLayer.ts | 40 +-- .../makeSeismicFenceMeshLayer.ts | 41 ++- .../SeismicCrosslineSetting.tsx | 2 +- .../SeismicFenceMeshLayer.ts | 258 ++++++++++++------ .../_private/ExtendedSimpleMeshLayer.ts | 2 + .../SeismicFenceMeshLayer/_private/worker.ts | 68 +++-- frontend/vite.config.ts | 10 + nginx.conf | 2 + 11 files changed, 287 insertions(+), 138 deletions(-) create mode 100644 frontend/src/assets/textures/checkboard.bmp create mode 100644 frontend/src/assets/textures/checkboard.png diff --git a/frontend/index.html b/frontend/index.html index 0c7933e06..e77241eb0 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - + Webviz | FMU results visualization diff --git a/frontend/src/assets/textures/checkboard.bmp b/frontend/src/assets/textures/checkboard.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6527087f3c76175ad9cf9f60f9053c56a8ebee81 GIT binary patch literal 40056 zcmeI!u}y|VZpe%JNq zbANv9_kDja>%4zk>$3lUdcD2tANI#_98d53_P!pU?;TG}{u$wd3lDA$d29wQxWoya zoatnE*NOcMdz^8$^X$B|R}b`{+XD`9sOwOAR41=^#VfZ;nxF?WJqYhQv7cd&GtPFN zotO6NfgW^wz#$HG9ZHYt%@MBJN=Dj z)yXSf@ye}|Cg{OT55l`n>}S~HjI*6*=cT=RpaN`c*QHXN}8YtGd&3JIVY0~d$86Y=8WG; literal 0 HcmV?d00001 diff --git a/frontend/src/assets/textures/checkboard.png b/frontend/src/assets/textures/checkboard.png new file mode 100644 index 0000000000000000000000000000000000000000..0b52d30aaad7a63bf105bc746acf8bfe3bc6b235 GIT binary patch literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^DIm2BR01_nk` zPZ!6KiaBqu8*(ul@-R4RKKUR2?Q5a*4&5}b#d(5m*2d{|bBb-xzKhF$_Rs$N&bW%H z3WiNbID|gy)@D4*@#ur_Ha`yXKYMe6gkus*r=ZHQrE;@3D}t3Nd-QnQnx#8~#oZ=I zOs~p0*9O!M6jw0R`+8#zFHk*D+%YNU)kaHch0|orNop18oAT2if~L fsDGd>I>YRmmvmC;m|7?>R2e*7{an^LB{Ts5s%DEX literal 0 HcmV?d00001 diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index 27742f196..719328501 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -61,12 +61,6 @@ export class RealizationSeismicCrosslineLayer return null; } - console.debug("real", { - x: [data.bbox_utm[0][0], data.bbox_utm[1][0]], - y: [data.bbox_utm[0][1], data.bbox_utm[1][1]], - z: [data.u_min, data.u_max], - }); - return { x: [data.bbox_utm[0][0], data.bbox_utm[1][0]], y: [data.bbox_utm[0][1], data.bbox_utm[1][1]], @@ -130,29 +124,23 @@ export class RealizationSeismicCrosslineLayer const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); const seismicCrosslineNumber = settings[SettingType.SEISMIC_CROSSLINE].getDelegate().getValue(); - const queryKey = [ - "realizationSeismicCrosslineSlice", - ensembleIdent, - seismicAttribute, - timeOrInterval, - realizationNum, - seismicCrosslineNumber, - ]; - this._layerDelegate.registerQueryKey(queryKey); + const queryOptions = getCrosslineSliceOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + realization_num: realizationNum ?? 0, + seismic_attribute: seismicAttribute ?? "", + time_or_interval_str: timeOrInterval ?? "", + observed: false, + crossline_no: seismicCrosslineNumber ?? 0, + }, + }); + + this._layerDelegate.registerQueryKey(queryOptions.queryKey); const seismicSlicePromise = queryClient .fetchQuery({ - ...getCrosslineSliceOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - realization_num: realizationNum ?? 0, - seismic_attribute: seismicAttribute ?? "", - time_or_interval_str: timeOrInterval ?? "", - observed: false, - crossline_no: seismicCrosslineNumber ?? 0, - }, - }), + ...queryOptions, }) .then((data) => transformSeismicSlice(data)); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index 2f61bce7e..5be03accd 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -73,7 +73,32 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { isLoading, predictedNextBoundingBox, }: VisualizationFunctionArgs): Layer { - let bbox = data.bbox_utm; + let bbox: number[][] = [ + [data.bbox_utm[0][0], data.bbox_utm[0][1], data.u_min], + [data.bbox_utm[1][0], data.bbox_utm[1][1], data.u_min], + [data.bbox_utm[0][0], data.bbox_utm[0][1], data.u_max], + [data.bbox_utm[1][0], data.bbox_utm[1][1], data.u_max], + ]; + + if (plane === Plane.DEPTH) { + bbox = [ + [data.bbox_utm[0][0], data.bbox_utm[0][1], settings.seismicDepthSlice], + [data.bbox_utm[0][0], data.bbox_utm[1][1], settings.seismicDepthSlice], + [data.bbox_utm[1][0], data.bbox_utm[0][1], settings.seismicDepthSlice], + [data.bbox_utm[1][0], data.bbox_utm[1][1], settings.seismicDepthSlice], + ]; + } + + if (isLoading && predictedNextBoundingBox) { + bbox = [ + [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], predictedNextBoundingBox.z[0]], + [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], predictedNextBoundingBox.z[0]], + [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], predictedNextBoundingBox.z[1]], + [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], predictedNextBoundingBox.z[1]], + ]; + } + + /* let adjustedData = data; if (isLoading && predictedNextBoundingBox) { bbox = [ @@ -130,19 +155,23 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { adjustedData.v_num_samples, transformUVToXYZ ); + */ return new MovableLayerWrapper({ wrappedLayer: new SeismicFenceMeshLayer({ id, name, data: { - vertices, - indices, - properties, + sections: [ + { + boundingBox: bbox, + properties: data.dataFloat32Arr, + numSamplesU: data.u_num_samples, + numSamplesV: data.v_num_samples, + }, + ], }, - startPosition, colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), - boundingBox, zIncreaseDownwards: true, isLoading, }), diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx index 5bac36a98..11aaa15ff 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx @@ -88,7 +88,7 @@ export class SeismicCrosslineSetting implements Setting { max={props.availableValues[1] ?? 1} onChange={handleSliderChange} value={props.value ?? props.availableValues[0] ?? 1} - debounceTimeMs={0} + debounceTimeMs={500} valueLabelDisplay="auto" />
diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts index 7de4fe564..ed170357e 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts @@ -1,3 +1,4 @@ +import checkboardTextureUrl from "@assets/textures/checkboard.bmp?url"; import { CompositeLayer, CompositeLayerProps, @@ -6,14 +7,14 @@ import { PickingInfo, UpdateParameters, } from "@deck.gl/core"; -import { PathLayer } from "@deck.gl/layers"; import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; import { Geometry } from "@luma.gl/engine"; +import { isEqual } from "lodash"; import workerpool from "workerpool"; import { ExtendedSimpleMeshLayer } from "./_private/ExtendedSimpleMeshLayer"; -import { Space, WebworkerParameters, makeMesh } from "./_private/worker"; +import { WebworkerParameters, makeMesh } from "./_private/worker"; export type SeismicFenceMeshLayerPickingInfo = { properties?: { name: string; value: number }[]; @@ -28,7 +29,6 @@ export type SeismicFenceSection = { export type SeismicFenceMeshLayerProps = { name?: string; - startPosition: [number, number, number]; data: { sections: SeismicFenceSection[]; }; @@ -38,6 +38,12 @@ export type SeismicFenceMeshLayerProps = { isLoading?: boolean; }; +function assert(condition: any, msg?: string): asserts condition { + if (!condition) { + throw new Error(msg); + } +} + export class SeismicFenceMeshLayer extends CompositeLayer { static layerName: string = "SeismicFenceMeshLayer"; @@ -52,15 +58,49 @@ export class SeismicFenceMeshLayer extends CompositeLayer>>) { + const updateRequired = + !isEqual(oldProps.data?.sections.length, props.data?.sections.length) || + !isEqual(oldProps.data?.sections, props.data?.sections); + + if (updateRequired) { + this.setState({ + ...this.state, + isLoaded: false, + }); + + if (props.isLoading) { + return; + } + + this.rebuildMesh(); + } + } + private calcNumVerticesForSection(section: SeismicFenceSection): number { return section.numSamplesU * section.numSamplesV * 3; } @@ -69,55 +109,90 @@ export class SeismicFenceMeshLayer extends CompositeLayer { + const verticesArr = new Float32Array(this._sharedVerticesBuffer!); + const indicesArr = new Uint32Array(this._sharedIndicesBuffer!); + this.setState({ + geometry: new Geometry({ + attributes: { + positions: verticesArr, + colors: { + value: this._colorsArray, + size: 4, + }, }, - }, - topology: "triangle-list", - indices: this._transIndices, - }), + topology: "triangle-list", + indices: indicesArr, + }), + isLoaded: true, + }); }); } } + private calcOrigin(): [number, number, number] { + const { data, zIncreaseDownwards } = this.props; + + if (data.sections.length === 0) { + return [0, 0, 0]; + } + + const firstSection = data.sections[0]; + + return [ + firstSection.boundingBox[0][0], + firstSection.boundingBox[0][1], + (zIncreaseDownwards ? -1 : 1) * firstSection.boundingBox[0][2], + ]; + } + private rebuildMesh() { - const params: WebworkerParameters[] = []; + const { zIncreaseDownwards } = this.props; + + this.initSharedBuffers(); + + assert(this._sharedVerticesBuffer !== null, "Shared vertices buffer is null"); + assert(this._sharedIndicesBuffer !== null, "Shared indices buffer is null"); - this.initTransferableObjects(); + const origin = this.calcOrigin(); let verticesIndex = 0; let indicesIndex = 0; for (const section of this.props.data.sections) { this._numTasks++; + const offset: [number, number, number] = [ + section.boundingBox[0][0] - origin[0], + section.boundingBox[0][1] - origin[1], + (zIncreaseDownwards ? -1 : 1) * section.boundingBox[0][2] - origin[2], + ]; + const params: WebworkerParameters = { + offset, numSamplesU: section.numSamplesU, numSamplesV: section.numSamplesV, boundingBox: section.boundingBox, startVerticesIndex: verticesIndex, startIndicesIndex: indicesIndex, - transVertices: this._transVertices, - transIndices: this._transIndices, - space: Space.FENCE, + sharedVerticesBuffer: this._sharedVerticesBuffer, + sharedIndicesBuffer: this._sharedIndicesBuffer, + zIncreasingDownwards: this.props.zIncreaseDownwards ?? false, }; verticesIndex += this.calcNumVerticesForSection(section); @@ -126,9 +201,8 @@ export class SeismicFenceMeshLayer extends CompositeLayer { - this; this._numTasksCompleted++; - this.checkIfAllTasksCompleted(); + this.maybeUpdateGeometry(); }) .catch(() => { this._numTasksFailed++; @@ -136,42 +210,42 @@ export class SeismicFenceMeshLayer extends CompositeLayer acc + section.properties.length * 4, 0) + ); - for (let i = 0; i < data.properties.length; i++) { - const [r, g, b] = colorMapFunction(data.properties[i]); - colors[i * 4 + 0] = r / 255; - colors[i * 4 + 1] = g / 255; - colors[i * 4 + 2] = b / 255; - colors[i * 4 + 3] = 1; + let colorIndex = 0; + for (const section of data.sections) { + for (let i = 0; i < section.properties.length; i++) { + const [r, g, b] = colorMapFunction(section.properties[i]); + this._colorsArray[colorIndex * 4 + 0] = r / 255; + this._colorsArray[colorIndex * 4 + 1] = g / 255; + this._colorsArray[colorIndex * 4 + 2] = b / 255; + this._colorsArray[colorIndex * 4 + 3] = 1; + colorIndex++; + } } - - return colors; } - private makeMesh() { + private getProperty(vertexIndex: number): number { const { data } = this.props; - this.setState({ - geometry: new Geometry({ - attributes: { - positions: data.vertices, - colors: { - value: this.makeColorsArray(), - size: 4, - }, - }, - topology: "triangle-list", - indices: data.indices, - }), - }); + let offset = 0; + for (const section of data.sections) { + if (vertexIndex < offset + section.properties.length) { + return section.properties[vertexIndex - offset]; + } + offset += section.properties.length; + } + + return 0; } getPickingInfo({ info }: GetPickingInfoParams): SeismicFenceMeshLayerPickingInfo { - const { data, zIncreaseDownwards } = this.props; + const { zIncreaseDownwards } = this.props; if (!info.color) { return info; } @@ -181,7 +255,8 @@ export class SeismicFenceMeshLayer extends CompositeLayer>>) { - super.updateState(params); - if (params.changeFlags.dataChanged) { - this.makeMesh(); - } - } - renderLayers() { - const { startPosition, boundingBox, hoverable, isLoading } = this.props; - const { geometry, isHovered } = this.state; + const { hoverable, isLoading, data, zIncreaseDownwards } = this.props; + const { geometry, isHovered, isLoaded } = this.state; + + const origin = this.calcOrigin(); const layers: Layer[] = []; - if (isLoading) { - layers.push( - new SimpleMeshLayer({ - id: "seismic-fence-mesh-layer-loading", - data: [0], - mesh: geometry, - getColor: [0, 0, 0, 50], - pickable: false, - getPosition: startPosition, - material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, - }) - ); + if (isLoading || !isLoaded) { + for (const section of data.sections) { + const vertices = new Float32Array(4 * 3); + const indices = new Uint32Array([0, 1, 2, 2, 3, 0]); + + let verticesIndex = 0; + vertices[verticesIndex++] = section.boundingBox[0][0]; + vertices[verticesIndex++] = section.boundingBox[0][1]; + vertices[verticesIndex++] = (zIncreaseDownwards ? -1 : 1) * section.boundingBox[0][2]; + + vertices[verticesIndex++] = section.boundingBox[1][0]; + vertices[verticesIndex++] = section.boundingBox[1][1]; + vertices[verticesIndex++] = (zIncreaseDownwards ? -1 : 1) * section.boundingBox[1][2]; + + vertices[verticesIndex++] = section.boundingBox[3][0]; + vertices[verticesIndex++] = section.boundingBox[3][1]; + vertices[verticesIndex++] = (zIncreaseDownwards ? -1 : 1) * section.boundingBox[3][2]; + + vertices[verticesIndex++] = section.boundingBox[2][0]; + vertices[verticesIndex++] = section.boundingBox[2][1]; + vertices[verticesIndex++] = (zIncreaseDownwards ? -1 : 1) * section.boundingBox[2][2]; + + const placeholderGeometry = new Geometry({ + attributes: { + positions: vertices, + }, + topology: "triangle-list", + indices, + }); + layers.push( + new SimpleMeshLayer({ + id: "seismic-fence-mesh-layer-loading", + data: [0], + mesh: placeholderGeometry, + getPosition: [0, 0, 0], + texture: checkboardTextureUrl, + getColor: [100, 100, 100, 100], + material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, + pickable: false, + }) + ); + } } else { layers.push( new ExtendedSimpleMeshLayer({ id: "seismic-fence-mesh-layer", data: [0], mesh: geometry, - getPosition: startPosition, + getPosition: origin, getColor: [255, 255, 255, 255], material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, pickable: true, @@ -243,6 +343,7 @@ export class SeismicFenceMeshLayer extends CompositeLayer [number, number, number] = () => { - throw new Error("transformUVToXYZ not implemented"); - }; - - if (parameters.space === Space.FENCE) { - transformUVToXYZ = (u: number, v: number): [number, number, number] => { - const x = v * (bbox[1][0] - bbox[0][0]); - const y = v * (bbox[1][1] - bbox[0][1]); - const z = -(u * (data.u_max - data.u_min)); - return [x, y, z]; - }; + function transformUVToXYZ(u: number, v: number): [number, number, number] { + const x = parameters.offset[0] + u * vectorU[0] + v * vectorV[0]; + const y = parameters.offset[1] + u * vectorU[1] + v * vectorV[1]; + const z = parameters.offset[2] + (parameters.zIncreasingDownwards ? -1 : 1) * (v * vectorV[2] + u * vectorU[2]); + return [x, y, z]; } -} -workerpool.worker({ - makeMesh, -}); + const verticesArray = new Float32Array(parameters.sharedVerticesBuffer); + const indicesArray = new Uint32Array(parameters.sharedIndicesBuffer); + + const stepU = 1.0 / (parameters.numSamplesU - 1); + const stepV = 1.0 / (parameters.numSamplesV - 1); + + let verticesIndex = parameters.startVerticesIndex; + let indicesIndex = parameters.startIndicesIndex; + + for (let v = 0; v < parameters.numSamplesV; v++) { + for (let u = 0; u < parameters.numSamplesU; u++) { + const [x, y, z] = transformUVToXYZ(u * stepU, v * stepV); + verticesArray[verticesIndex++] = x; + verticesArray[verticesIndex++] = y; + verticesArray[verticesIndex++] = z; + + if (u > 0 && v > 0) { + indicesArray[indicesIndex++] = (v - 1) * parameters.numSamplesU + u - 1; + indicesArray[indicesIndex++] = (v - 1) * parameters.numSamplesU + u; + indicesArray[indicesIndex++] = v * parameters.numSamplesU + u - 1; + + indicesArray[indicesIndex++] = v * parameters.numSamplesU + u - 1; + indicesArray[indicesIndex++] = (v - 1) * parameters.numSamplesU + u; + indicesArray[indicesIndex++] = v * parameters.numSamplesU + u; + } + } + } +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 4ab7e9cc7..b989713ed 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -29,6 +29,16 @@ export default defineConfig(({ mode }) => { return { plugins: [ + { + name: "isolation", + configureServer(server) { + server.middlewares.use((_req, res, next) => { + res.setHeader("Cross-Origin-Opener-Policy", "same-origin"); + res.setHeader("Cross-Origin-Embedder-Policy", "require-corp"); + next(); + }); + }, + }, react({ babel: { plugins: [jotaiDebugLabel, jotaiReactRefresh], diff --git a/nginx.conf b/nginx.conf index 63e284a14..b0fb8c137 100644 --- a/nginx.conf +++ b/nginx.conf @@ -64,6 +64,8 @@ http { add_header X-Frame-Options "DENY"; add_header X-XSS-Protection "1; mode=block"; add_header Referrer-Policy "no-referrer"; + add_header "Cross-Origin-Embedder-Policy" "require-corp"; + add_header "Cross-Origin-Opener-Policy" "same-origin; "; } } } From 8ebdb4d9876981171ec8f602a0eb3e9e4bc9a403 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 13 Feb 2025 00:33:38 +0100 Subject: [PATCH 51/97] wip --- .../SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts index ed170357e..abf998007 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts @@ -1,4 +1,3 @@ -import checkboardTextureUrl from "@assets/textures/checkboard.bmp?url"; import { CompositeLayer, CompositeLayerProps, @@ -322,7 +321,6 @@ export class SeismicFenceMeshLayer extends CompositeLayer Date: Thu, 13 Feb 2025 17:12:53 +0100 Subject: [PATCH 52/97] wip --- frontend/src/assets/textures/checkboard.bmp | Bin 40056 -> 0 bytes frontend/src/assets/textures/checkboard.png | Bin 387 -> 0 bytes frontend/src/assets/textures/checkerboard.png | Bin 0 -> 197 bytes .../makeSeismicFenceMeshLayer.ts | 35 ++- .../view/components/ReadoutWrapper.tsx | 36 +++- .../3DViewerNew/view/utils/LabelOrganizer.tsx | 125 +++++++++++ .../SeismicCrosslineSetting.tsx | 6 +- .../SeismicDepthSliceSetting.tsx | 6 +- .../implementations/SeismicInlineSetting.tsx | 6 +- .../deckgl/wellborePicksLayer.ts | 2 +- .../customDeckGlLayers/DragHandleLayer.ts | 200 ------------------ .../customDeckGlLayers/MeshLayer/MeshLayer.ts | 174 --------------- .../MeshLayer/_shaders/fragment.glsl | 37 ---- .../MeshLayer/_shaders/vertex.glsl | 47 ---- .../customDeckGlLayers/MeshLayer/utils.ts | 49 ----- .../customDeckGlLayers/MovableLayerWrapper.ts | 34 --- .../SeismicFenceMeshLayer.ts | 37 +++- .../_private/ExtendedSimpleMeshLayer.ts | 1 + .../customDeckGlLayers/WellborePicksLayer.ts | 59 +++++- 19 files changed, 277 insertions(+), 577 deletions(-) delete mode 100644 frontend/src/assets/textures/checkboard.bmp delete mode 100644 frontend/src/assets/textures/checkboard.png create mode 100644 frontend/src/assets/textures/checkerboard.png create mode 100644 frontend/src/modules/3DViewerNew/view/utils/LabelOrganizer.tsx delete mode 100644 frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts delete mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts delete mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl delete mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl delete mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts delete mode 100644 frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts diff --git a/frontend/src/assets/textures/checkboard.bmp b/frontend/src/assets/textures/checkboard.bmp deleted file mode 100644 index 6527087f3c76175ad9cf9f60f9053c56a8ebee81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40056 zcmeI!u}y|VZpe%JNq zbANv9_kDja>%4zk>$3lUdcD2tANI#_98d53_P!pU?;TG}{u$wd3lDA$d29wQxWoya zoatnE*NOcMdz^8$^X$B|R}b`{+XD`9sOwOAR41=^#VfZ;nxF?WJqYhQv7cd&GtPFN zotO6NfgW^wz#$HG9ZHYt%@MBJN=Dj z)yXSf@ye}|Cg{OT55l`n>}S~HjI*6*=cT=RpaN`c*QHXN}8YtGd&3JIVY0~d$86Y=8WG; diff --git a/frontend/src/assets/textures/checkboard.png b/frontend/src/assets/textures/checkboard.png deleted file mode 100644 index 0b52d30aaad7a63bf105bc746acf8bfe3bc6b235..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^DIm2BR01_nk` zPZ!6KiaBqu8*(ul@-R4RKKUR2?Q5a*4&5}b#d(5m*2d{|bBb-xzKhF$_Rs$N&bW%H z3WiNbID|gy)@D4*@#ur_Ha`yXKYMe6gkus*r=ZHQrE;@3D}t3Nd-QnQnx#8~#oZ=I zOs~p0*9O!M6jw0R`+8#zFHk*D+%YNU)kaHch0|orNop18oAT2if~L fsDGd>I>YRmmvmC;m|7?>R2e*7{an^LB{Ts5s%DEX diff --git a/frontend/src/assets/textures/checkerboard.png b/frontend/src/assets/textures/checkerboard.png new file mode 100644 index 0000000000000000000000000000000000000000..ed5994dbd2e8c65f96ddcdeaba71043190baf938 GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^N+8U^1|+TAxeoy;&H|6fVg?31We{epSZZGe6fF02 zaSVxQeS2*qZ-aq=b0Bx1f%CG~T0Lt6FAlG?1>8bQHSaF@FnOKr-%AVzQ%(z}zWByE zxqzb>2}TzmS!&~<*?VvIm&fZ$o2@dVvR!4v8Eks$WDX(VGf6iFzNZJxvl`r2F`N@W Ydv<4W+2j7DKqoMGy85}Sb4q9e01)3nQUCw| literal 0 HcmV?d00001 diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index 5be03accd..83ff8db9c 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -2,7 +2,6 @@ import { Layer } from "@deck.gl/core"; import { SeismicSliceData_trans } from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; -import { MovableLayerWrapper } from "@modules/_shared/customDeckGlLayers/MovableLayerWrapper"; import { SeismicFenceMeshLayer } from "@modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer"; /* @@ -157,24 +156,22 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { ); */ - return new MovableLayerWrapper({ - wrappedLayer: new SeismicFenceMeshLayer({ - id, - name, - data: { - sections: [ - { - boundingBox: bbox, - properties: data.dataFloat32Arr, - numSamplesU: data.u_num_samples, - numSamplesV: data.v_num_samples, - }, - ], - }, - colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), - zIncreaseDownwards: true, - isLoading, - }), + return new SeismicFenceMeshLayer({ + id, + name, + data: { + sections: [ + { + boundingBox: bbox, + properties: data.dataFloat32Arr, + numSamplesU: data.u_num_samples, + numSamplesV: data.v_num_samples, + }, + ], + }, + colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), + zIncreaseDownwards: true, + isLoading, }); }; } diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index ac2b70f35..107d947ec 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { Layer as DeckGlLayer } from "@deck.gl/core"; +import { Layer as DeckGlLayer, View as DeckGlView } from "@deck.gl/core"; import { DeckGLRef } from "@deck.gl/react"; import { useIntersectionPolylines } from "@framework/UserCreatedItems"; import { WorkbenchSession } from "@framework/WorkbenchSession"; @@ -18,6 +18,7 @@ import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; import { Toolbar } from "./Toolbar"; import { DeckGlInstanceManager, DeckGlInstanceManagerTopic } from "../utils/DeckGlInstanceManager"; +import { LabelComponent, LabelOrganizer } from "../utils/LabelOrganizer"; import { Polyline, PolylinesPlugin, PolylinesPluginTopic } from "../utils/PolylinesPlugin"; export type ReadooutWrapperProps = { @@ -32,9 +33,11 @@ export type ReadooutWrapperProps = { export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const id = React.useId(); const deckGlRef = React.useRef(null); + deckGlRef.current?.deck?.needsRedraw; const [deckGlManager, setDeckGlManager] = React.useState( new DeckGlInstanceManager(deckGlRef.current) ); + const [labelOrganizer, setLabelOrganizer] = React.useState(new LabelOrganizer(deckGlRef.current)); const [polylinesPlugin, setPolylinesPlugin] = React.useState(new PolylinesPlugin(deckGlManager)); usePublishSubscribeTopicValue(deckGlManager, DeckGlInstanceManagerTopic.REDRAW); @@ -65,6 +68,8 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const manager = new DeckGlInstanceManager(deckGlRef.current); setDeckGlManager(manager); + labelOrganizer.setDeckRef(deckGlRef.current); + const polylinesPlugin = new PolylinesPlugin(manager, colorGenerator()); polylinesPlugin.setPolylines(intersectionPolylines.getPolylines()); manager.addPlugin(polylinesPlugin); @@ -91,7 +96,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { unsubscribeFromIntersectionPolylines(); }; }, - [intersectionPolylines, colorGenerator] + [intersectionPolylines, colorGenerator, labelOrganizer] ); const [cameraPositionSetByAction, setCameraPositionSetByAction] = React.useState(null); @@ -142,11 +147,34 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { setLayerPickingInfo(pickingInfo); } - let adjustedLayers = [...props.layers]; + let adjustedLayers: DeckGlLayer[] = []; + for (const layer of props.layers) { + adjustedLayers.push( + layer.clone({ + // @ts-expect-error - we need to add the registerLabels function to the layer + reportLabels: labelOrganizer.registerLabels.bind(labelOrganizer), + }) + ); + } if (!gridVisible) { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); } + const viewportLabels = labelOrganizer.makeLabelComponents(); + const viewportAnnotations = viewportLabels.map((el) => { + return ( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + /* @ts-expect-error */ + +
+ {el.labels.map((label) => ( + + ))} +
+
+ ); + }); + return ( <> setCameraPositionSetByAction(null), verticalScale, @@ -190,6 +219,7 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { })} > {props.viewportAnnotations} + {viewportAnnotations} {props.views.viewports.length === 0 && (
diff --git a/frontend/src/modules/3DViewerNew/view/utils/LabelOrganizer.tsx b/frontend/src/modules/3DViewerNew/view/utils/LabelOrganizer.tsx new file mode 100644 index 000000000..0f2e86998 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/utils/LabelOrganizer.tsx @@ -0,0 +1,125 @@ +import React from "react"; + +import { DeckGLRef } from "@deck.gl/react"; +import { Vec2 } from "@lib/utils/vec2"; + +export type Label = { + name: string; + referencePosition: [number, number, number]; +}; + +export class LabelOrganizer { + private _labelsMap: Map = new Map(); + private _animationFrameId: number | null = null; + private _subscribers: (() => void)[] = []; + + private _ref: DeckGLRef | null; + + constructor(ref: DeckGLRef | null) { + this._ref = ref; + } + + setDeckRef(ref: DeckGLRef | null) { + this._ref = ref; + if (this._ref?.deck) { + this._ref.deck.props.onViewStateChange = () => { + this.maybeUpdateLabels(); + }; + } + } + + private maybeUpdateLabels() { + this._subscribers.forEach((s) => s()); + } + + subscribe(func: () => void) { + this._subscribers.push(func); + } + + registerLabels(layerId: string, labels: Label[]) { + this._labelsMap.set(layerId, labels); + } + + makeLabelComponents(): { viewportId: string; labels: LabelComponentProps[] }[] { + const viewports = this._ref?.deck?.getViewports(); + if (!viewports) { + return []; + } + + this._subscribers = []; + + const result: { viewportId: string; labels: LabelComponentProps[] }[] = []; + + for (const [layerId, labels] of this._labelsMap) { + for (const viewport of viewports) { + const projectFunc = viewport.project; + const viewportId = viewport.id; + const viewportLabels: LabelComponentProps[] = labels.map((label) => { + return { + id: label.name, + name: label.name, + referencePosition: label.referencePosition, + position: label.referencePosition, + projectFunc: (position: [number, number, number]) => { + const pos = projectFunc(position); + return { + x: pos[0], + y: pos[1], + }; + }, + subscribeToViewportChange: this.subscribe.bind(this), + }; + }); + + const existing = result.find((r) => r.viewportId === viewportId); + if (existing) { + existing.labels.push(...viewportLabels); + continue; + } + + result.push({ + viewportId, + labels: viewportLabels, + }); + } + } + + return result; + } +} + +type LabelComponentProps = { + id: string; + name: string; + referencePosition: [number, number, number]; + position: [number, number, number]; + projectFunc: (position: [number, number, number]) => Vec2; + subscribeToViewportChange: (func: () => void) => void; +}; + +export function LabelComponent(props: LabelComponentProps) { + const [position, setPosition] = React.useState(props.projectFunc(props.referencePosition)); + + React.useEffect(() => { + props.subscribeToViewportChange(() => { + const newPosition = props.projectFunc(props.referencePosition); + setPosition(newPosition); + }); + }, [props.projectFunc, props.referencePosition, props.subscribeToViewportChange]); + + return ( +
+ {props.name} +
+ ); +} diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx index 11aaa15ff..182af9411 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx @@ -80,6 +80,8 @@ export class SeismicCrosslineSetting implements Setting { props.onValueChange(Number(event.target.value)); } + const validValue = props.value ?? props.availableValues[0] ?? 1; + return (
@@ -87,13 +89,13 @@ export class SeismicCrosslineSetting implements Setting { min={0} max={props.availableValues[1] ?? 1} onChange={handleSliderChange} - value={props.value ?? props.availableValues[0] ?? 1} + value={validValue} debounceTimeMs={500} valueLabelDisplay="auto" />
- +
); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicDepthSliceSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicDepthSliceSetting.tsx index 7b3eafacb..169308623 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicDepthSliceSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicDepthSliceSetting.tsx @@ -92,6 +92,8 @@ export class SeismicDepthSliceSetting implements Setting { props.onValueChange(closestValue); } + const validValue = props.value ?? props.availableValues[0] ?? 1; + return (
@@ -99,14 +101,14 @@ export class SeismicDepthSliceSetting implements Setting { min={props.availableValues[0]} max={props.availableValues[1]} onChange={handleSliderChange} - value={props.value ?? props.availableValues[0] ?? 1} + value={validValue} debounceTimeMs={500} valueLabelDisplay="auto" step={props.availableValues[2]} />
- +
); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx index b6dfa52e4..14f66af1f 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx @@ -80,6 +80,8 @@ export class SeismicInlineSetting implements Setting { props.onValueChange(Number(event.target.value)); } + const validValue = props.value ?? props.availableValues[0] ?? 1; + return (
@@ -87,13 +89,13 @@ export class SeismicInlineSetting implements Setting { min={0} max={props.availableValues[1] ?? 1} onChange={handleSliderChange} - value={props.value ?? props.availableValues[0] ?? 1} + value={validValue} debounceTimeMs={500} valueLabelDisplay="auto" />
- +
); diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer.ts index 637b96b0e..a402295b9 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer.ts @@ -11,7 +11,7 @@ export function makeWellborePicksLayer({ return { easting: wellborePick.easting, northing: wellborePick.northing, - wellBoreUwi: wellborePick.wellboreUuid, + wellBoreUwi: wellborePick.uniqueWellboreIdentifier, tvdMsl: wellborePick.tvdMsl, md: wellborePick.md, slotName: "", diff --git a/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts deleted file mode 100644 index 600ee4456..000000000 --- a/frontend/src/modules/_shared/customDeckGlLayers/DragHandleLayer.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { CompositeLayer, PickingInfo } from "@deck.gl/core"; -import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; -import { Geometry } from "@luma.gl/engine"; - -export enum DragDirection { - X = "X", - Y = "Y", - Z = "Z", - XY = "XY", - XZ = "XZ", - YZ = "YZ", - XYZ = "XYZ", -} - -export type DragHandleLayerProps = { - dragDirection: DragDirection; - position: [number, number, number]; -}; - -function makeArrowMesh(): Geometry { - const numCircleVertices = 20; - const vertices = new Float32Array((numCircleVertices + 1) * 4 * 3); // 4x circle with midpoint - const indices = new Uint32Array(numCircleVertices * 3 * 3 + numCircleVertices * 2 * 3); - let triangleIndex = 0; - let vertexIndex = 0; - const coneRadius = 2; - const shaftRadius = 1; - const coneHeight = 3; - const shaftHeight = 3; - - // The tip of the cone - vertices[vertexIndex++] = 0; - vertices[vertexIndex++] = 0; - vertices[vertexIndex++] = coneHeight + shaftHeight; - - // Cone - for (let i = 0; i < numCircleVertices; i++) { - const angle = (i / numCircleVertices) * Math.PI * 2; - vertices[vertexIndex++] = Math.cos(angle) * coneRadius; - vertices[vertexIndex++] = Math.sin(angle) * coneRadius; - vertices[vertexIndex++] = shaftHeight; - - indices[triangleIndex++] = 0; - indices[triangleIndex++] = i + 1; - indices[triangleIndex++] = i === numCircleVertices - 1 ? 1 : i + 2; - } - - // The base of the cone - const baseStartIndex = vertexIndex / 3; - - vertices[vertexIndex++] = 0; - vertices[vertexIndex++] = 0; - vertices[vertexIndex++] = shaftHeight; - - for (let i = 0; i < numCircleVertices; i++) { - const angle = (i / numCircleVertices) * Math.PI * 2; - vertices[vertexIndex++] = Math.cos(angle) * coneRadius; - vertices[vertexIndex++] = Math.sin(angle) * coneRadius; - vertices[vertexIndex++] = shaftHeight; - - indices[triangleIndex++] = baseStartIndex; - indices[triangleIndex++] = baseStartIndex + i + 1; - indices[triangleIndex++] = i === numCircleVertices - 1 ? baseStartIndex + 1 : baseStartIndex + i + 2; - } - - // The shaft top of the arrow - const shaftTopStartIndex = vertexIndex / 3; - - vertices[vertexIndex++] = 0; - vertices[vertexIndex++] = 0; - vertices[vertexIndex++] = shaftHeight; - - for (let i = 0; i < numCircleVertices; i++) { - const angle = (i / numCircleVertices) * Math.PI * 2; - vertices[vertexIndex++] = Math.cos(angle) * shaftRadius; - vertices[vertexIndex++] = Math.sin(angle) * shaftRadius; - vertices[vertexIndex++] = shaftHeight; - } - - // The shaft bottom of the arrow - const shaftBottomStartIndex = vertexIndex / 3; - - vertices[vertexIndex++] = 0; - vertices[vertexIndex++] = 0; - vertices[vertexIndex++] = 0; - - for (let i = 0; i < numCircleVertices; i++) { - const angle = (i / numCircleVertices) * Math.PI * 2; - vertices[vertexIndex++] = Math.cos(angle) * shaftRadius; - vertices[vertexIndex++] = Math.sin(angle) * shaftRadius; - vertices[vertexIndex++] = 0; - - indices[triangleIndex++] = shaftBottomStartIndex; - indices[triangleIndex++] = shaftBottomStartIndex + i + 1; - indices[triangleIndex++] = - i === numCircleVertices - 1 ? shaftBottomStartIndex + 1 : shaftBottomStartIndex + i + 2; - } - - // The sides of the shaft - for (let i = 0; i < numCircleVertices; i++) { - indices[triangleIndex++] = shaftTopStartIndex + i + 1; - indices[triangleIndex++] = - i === numCircleVertices - 1 ? shaftBottomStartIndex + 1 : shaftBottomStartIndex + i + 2; - indices[triangleIndex++] = shaftBottomStartIndex + 1 + i; - - indices[triangleIndex++] = shaftTopStartIndex + i + 1; - indices[triangleIndex++] = i === numCircleVertices - 1 ? shaftTopStartIndex + 1 : shaftTopStartIndex + i + 2; - indices[triangleIndex++] = - i === numCircleVertices - 1 ? shaftBottomStartIndex + 1 : shaftBottomStartIndex + i + 2; - } - - return new Geometry({ - attributes: { - positions: vertices, - }, - topology: "triangle-list", - indices: indices, - }); -} - -export class DragHandleLayer extends CompositeLayer { - static layerName: string = "DragHandleLayer"; - - // @ts-expect-error - expected - state!: { - hoveredIndex: number; - }; - - initializeState(): void { - this.state = { - hoveredIndex: -1, - }; - } - - onHover(info: PickingInfo): boolean { - this.setState({ - hoveredIndex: info.index, - }); - - return true; - } - - renderLayers() { - const { position, dragDirection } = this.props; - const { hoveredIndex } = this.state; - - const data: { position: number[]; orientation: number[] }[] = []; - - if (dragDirection === DragDirection.X) { - data.push({ position: [position[0] + 10, position[1], position[2]], orientation: [90, 0, 0] }); - data.push({ position: [position[0] - 10, position[1], position[2]], orientation: [-90, 0, 0] }); - } else if (dragDirection === DragDirection.Y) { - data.push({ position: [position[0], position[1] + 10, position[2]], orientation: [90, 90, 0] }); - data.push({ position: [position[0], position[1] - 10, position[2]], orientation: [90, -90, 0] }); - } else if (dragDirection === DragDirection.Z) { - data.push({ position: [position[0], position[1], position[2] + 10], orientation: [0, 0, 0] }); - data.push({ position: [position[0], position[1], position[2] - 10], orientation: [180, 0, 0] }); - } else if (dragDirection === DragDirection.XY) { - data.push({ position: [position[0] + 10, position[1], position[2]], orientation: [90, 0, 0] }); - data.push({ position: [position[0] - 10, position[1], position[2]], orientation: [-90, 0, 0] }); - data.push({ position: [position[0], position[1] + 10, position[2]], orientation: [0, 90, 0] }); - data.push({ position: [position[0], position[1] - 10, position[2]], orientation: [0, -90, 0] }); - } else if (dragDirection === DragDirection.XZ) { - data.push({ position: [position[0] + 10, position[1], position[2]], orientation: [90, 0, 0] }); - data.push({ position: [position[0] - 10, position[1], position[2]], orientation: [-90, 0, 0] }); - data.push({ position: [position[0], position[1], position[2] + 10], orientation: [0, 0, 0] }); - data.push({ position: [position[0], position[1], position[2] - 10], orientation: [180, 0, 0] }); - } else if (dragDirection === DragDirection.YZ) { - data.push({ position: [position[0], position[1] + 10, position[2]], orientation: [0, 90, 0] }); - data.push({ position: [position[0], position[1] - 10, position[2]], orientation: [0, -90, 0] }); - data.push({ position: [position[0], position[1], position[2] + 10], orientation: [0, 0, 0] }); - data.push({ position: [position[0], position[1], position[2] - 10], orientation: [180, 0, 0] }); - } else if (dragDirection === DragDirection.XYZ) { - data.push({ position: [position[0] + 10, position[1], position[2]], orientation: [90, 0, 0] }); - data.push({ position: [position[0] - 10, position[1], position[2]], orientation: [-90, 0, 0] }); - data.push({ position: [position[0], position[1] + 10, position[2]], orientation: [90, 90, 0] }); - data.push({ position: [position[0], position[1] - 10, position[2]], orientation: [90, -90, 0] }); - data.push({ position: [position[0], position[1], position[2] + 10], orientation: [0, 0, 0] }); - data.push({ position: [position[0], position[1], position[2] - 10], orientation: [180, 0, 0] }); - } - - const layers = [ - new SimpleMeshLayer({ - id: "drag-handle-cone", - data, - mesh: makeArrowMesh(), - getPosition: (d) => d.position, - getColor: (d, ctx) => (ctx.index === hoveredIndex ? [255, 255, 255] : [0, 0, 255]), - getOrientation: (d) => d.orientation, - getScale: [10, 10, 10], - pickable: true, - updateTriggers: { - getColor: [hoveredIndex], - }, - }), - ]; - - return layers; - } -} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts deleted file mode 100644 index d3f90173d..000000000 --- a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/MeshLayer.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { - Accessor, - Color, - DefaultProps, - Layer, - LayerProps, - Material, - Position, - UpdateParameters, - picking, - project32, -} from "@deck.gl/core"; -import { getMeshBoundingBox } from "@loaders.gl/schema"; -import { Model } from "@luma.gl/engine"; -import { phongMaterial } from "@luma.gl/shadertools"; -import { utilities } from "@webviz/subsurface-viewer/dist/layers/shader_modules"; - -import fs from "./_shaders/fragment.glsl?raw"; -import vs from "./_shaders/vertex.glsl?raw"; -import { getGeometry } from "./utils"; - -import { Mesh } from "../types"; - -type _MeshLayerProps = { - mesh: string | Mesh | Promise | null; - getPosition?: Accessor; - getColor?: Accessor; - getOrientation?: Accessor; - getScale?: Accessor; - getTranslation?: Accessor; - sizeScale?: number; - material?: Material; - wireframe?: boolean; - // _instanced is a hack to use world position instead of meter offsets in mesh - // TODO - formalize API - _instanced: true; -}; - -export type MeshLayerProps = _MeshLayerProps & LayerProps; - -const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255]; - -const defaultProps: DefaultProps = { - mesh: { type: "object", value: null, async: true }, - getPosition: { type: "accessor", value: (d: any) => d.position }, - getColor: { type: "accessor", value: DEFAULT_COLOR }, - getOrientation: { type: "accessor", value: [0, 0, 0] }, - getScale: { type: "accessor", value: [1, 1, 1] }, - getTranslation: { type: "accessor", value: [0, 0, 0] }, - material: true, - wireframe: false, - _instanced: true, -}; - -export class MeshLayer extends Layer< - TExtraProps & Required<_MeshLayerProps> -> { - static layerName: string = "MeshLayer"; - static defaultProps = defaultProps; - - // @ts-expect-error - state is working in deck.gl - state!: { - model?: Model; - hasNormals?: boolean; - positionBounds?: [number[], number[]] | null; - }; - - getShaders() { - return super.getShaders({ - vs, - fs, - modules: [project32, phongMaterial, picking, utilities], - }); - } - - getBounds(): [number[], number[]] | null { - if (this.props._instanced) { - return super.getBounds(); - } - let result = this.state.positionBounds; - if (result) { - return result; - } - const { mesh } = this.props; - if (!mesh) { - return null; - } - // @ts-ignore Detect if mesh is generated by loaders.gl - result = mesh.header?.boundingBox; - - if (!result) { - // Otherwise, calculate bounding box from positions - const { attributes } = getGeometry(mesh as Mesh); - attributes.POSITION = attributes.POSITION || attributes.positions; - - //@ts-expect-error - result = getMeshBoundingBox(attributes); - } - - this.state.positionBounds = result; - return result; - } - - initializeState() { - const attributeManager = this.getAttributeManager(); - // attributeManager is always defined in a primitive layer - attributeManager!.addInstanced({ - instancePositions: { - transition: true, - type: "float64", - fp64: this.use64bitPositions(), - size: 3, - accessor: "getPosition", - }, - }); - } - - updateState(params: UpdateParameters) { - super.updateState(params); - - const { props, oldProps, changeFlags } = params; - if (props.mesh !== oldProps.mesh || changeFlags.extensionsChanged) { - this.state.positionBounds = null; - this.state.model?.destroy(); - if (props.mesh) { - this.state.model = this.getModel(props.mesh as Mesh); - - const attributes = (props.mesh as any).attributes || props.mesh; - this.setState({ - hasNormals: Boolean(attributes.NORMAL || attributes.normals), - }); - } - // attributeManager is always defined in a primitive layer - this.getAttributeManager()!.invalidateAll(); - } - - if (this.state.model) { - this.state.model.setTopology(this.props.wireframe ? "line-strip" : "triangle-list"); - } - } - - draw({ uniforms }: { uniforms: any }) { - const { model } = this.state; - if (!model) { - return; - } - - const { renderPass } = this.context; - const { sizeScale } = this.props; - - model.setUniforms(uniforms); - model.setUniforms({ - sizeScale, - flatShading: !this.state.hasNormals, - }); - model.draw(renderPass); - } - - get isLoaded(): boolean { - return Boolean(this.state?.model && super.isLoaded); - } - - protected getModel(mesh: Mesh): Model { - const model = new Model(this.context.device, { - ...this.getShaders(), - id: this.props.id, - bufferLayout: this.getAttributeManager()!.getBufferLayouts(), - geometry: getGeometry(mesh), - isInstanced: true, - }); - - return model; - } -} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl deleted file mode 100644 index 5b30bd1ef..000000000 --- a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/fragment.glsl +++ /dev/null @@ -1,37 +0,0 @@ -#version 300 es -#define SHADER_NAME mesh-layer-fs - -precision highp float; - -in vec3 cameraPosition; -in vec3 normals_commonspace; -in vec4 position_commonspace; -in vec4 vColor; -flat in int vertexIndex; -uniform bool flatShading; -uniform float opacity; - -out vec4 fragColor; - -void main(void) { - vec3 normal; - - if (flatShading) { - normal = normalize(cross(dFdx(position_commonspace.xyz), dFdy(position_commonspace.xyz))); - } else { - normal = normals_commonspace; - } - - // Picking pass - if (picking.isActive > 0.5 && !(picking.isAttribute > 0.5)) { - fragColor = encodeVertexIndexToRGB(vertexIndex); - return; - } - - vec4 color = vec4(vColor.rgb, 1.0); - - DECKGL_FILTER_COLOR(color, geometry); - - vec3 lightColor = lighting_getLightColor(color.rgb, cameraPosition, position_commonspace.xyz, normal); - fragColor = vec4(lightColor, color.a * opacity); -} \ No newline at end of file diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl deleted file mode 100644 index 298b34156..000000000 --- a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/_shaders/vertex.glsl +++ /dev/null @@ -1,47 +0,0 @@ -#version 300 es -#define SHADER_NAME mesh-layer-vs - -// Scale the model -uniform float sizeScale; - -// Primitive attributes -in vec3 positions; -in vec3 normals; -in vec3 colors; - -// Instance attributes -in vec3 instancePositions; -in vec3 instancePositions64Low; -in vec3 instanceTranslation; - -// Outputs to fragment shader -out vec3 cameraPosition; -out vec3 normals_commonspace; -out vec4 position_commonspace; -out vec4 vColor; -flat out int vertexIndex; - - -void main(void) { - geometry.worldPosition = instancePositions; - geometry.pickingColor = vec3(1.0, 1.0, 0.0); - - vertexIndex = gl_VertexID; - - cameraPosition = project_uCameraPosition; - - vColor = vec4(colors.rgb, 1.0); - - vec3 pos = positions * sizeScale + instanceTranslation; - - pos = project_size(pos); - DECKGL_FILTER_SIZE(pos, geometry); - gl_Position = project_position_to_clipspace(instancePositions, instancePositions64Low, pos, position_commonspace); - geometry.position = position_commonspace; - normals_commonspace = project_normal(normals); - - geometry.normal = normals_commonspace; - DECKGL_FILTER_GL_POSITION(gl_Position, geometry); - - DECKGL_FILTER_COLOR(vColor, geometry); -} \ No newline at end of file diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts b/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts deleted file mode 100644 index 3fbacd566..000000000 --- a/frontend/src/modules/_shared/customDeckGlLayers/MeshLayer/utils.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { log } from "@deck.gl/core"; -import { MeshAttributes } from "@loaders.gl/schema"; -import { Geometry } from "@luma.gl/engine"; - -import { Mesh } from "../types"; - -function normalizeGeometryAttributes(attributes: MeshAttributes): MeshAttributes { - const positionAttribute = attributes.positions || attributes.POSITION; - log.assert(positionAttribute, 'no "postions" or "POSITION" attribute in mesh'); - - const vertexCount = positionAttribute.value.length / positionAttribute.size; - let colorAttribute = attributes.COLOR_0 || attributes.colors; - if (!colorAttribute) { - colorAttribute = { size: 3, value: new Float32Array(vertexCount * 3).fill(1) }; - } - let normalAttribute = attributes.NORMAL || attributes.normals; - if (!normalAttribute) { - normalAttribute = { size: 3, value: new Float32Array(vertexCount * 3).fill(0) }; - } - - return { - positions: positionAttribute, - colors: colorAttribute, - normals: normalAttribute, - }; -} - -/* - * Convert mesh data into geometry - * @returns {Geometry} geometry - */ -export function getGeometry(data: Mesh): Geometry { - if (data instanceof Geometry) { - // @ts-expect-error data.attributes is readonly - data.attributes = normalizeGeometryAttributes(data.attributes); - return data; - } else if ((data as any).attributes) { - return new Geometry({ - ...data, - topology: "triangle-list", - attributes: normalizeGeometryAttributes((data as any).attributes), - }); - } else { - return new Geometry({ - topology: "triangle-list", - attributes: normalizeGeometryAttributes(data as MeshAttributes), - }); - } -} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts b/frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts deleted file mode 100644 index 6e08e97a7..000000000 --- a/frontend/src/modules/_shared/customDeckGlLayers/MovableLayerWrapper.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { CompositeLayer, GetPickingInfoParams, Layer, LayersList, PickingInfo } from "@deck.gl/core"; - -import { DragDirection } from "./DragHandleLayer"; - -export type MovableLayerWrapperProps, TProps extends Record> = { - wrappedLayer: TLayer; - draggable?: boolean; - dragDirection?: DragDirection; -}; - -export type MovableLayerPickingInfo = { - isMovable: boolean; -} & PickingInfo; - -export class MovableLayerWrapper< - TLayer extends Layer, - TProps extends Record -> extends CompositeLayer> { - static layerName: string = "MovableLayerWrapper"; - - getPickingInfo({ info }: GetPickingInfoParams): MovableLayerPickingInfo { - const { wrappedLayer } = this.props; - return { - ...info, - layer: wrappedLayer, - isMovable: true, - }; - } - - renderLayers(): Layer | null | LayersList { - const { wrappedLayer } = this.props; - return wrappedLayer; - } -} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts index abf998007..92b0b036e 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts @@ -8,6 +8,8 @@ import { } from "@deck.gl/core"; import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; import { Geometry } from "@luma.gl/engine"; +import { ExtendedLayerProps } from "@webviz/subsurface-viewer"; +import { BoundingBox3D, ReportBoundingBoxAction } from "@webviz/subsurface-viewer/dist/components/Map"; import { isEqual } from "lodash"; import workerpool from "workerpool"; @@ -26,8 +28,7 @@ export type SeismicFenceSection = { boundingBox: number[][]; // [minX, minY, minZ, maxX, maxY, maxZ] }; -export type SeismicFenceMeshLayerProps = { - name?: string; +export interface SeismicFenceMeshLayerProps extends ExtendedLayerProps { data: { sections: SeismicFenceSection[]; }; @@ -35,7 +36,10 @@ export type SeismicFenceMeshLayerProps = { hoverable?: boolean; zIncreaseDownwards?: boolean; isLoading?: boolean; -}; + + // Non public properties: + reportBoundingBox?: React.Dispatch; +} function assert(condition: any, msg?: string): asserts condition { if (!condition) { @@ -122,6 +126,28 @@ export class SeismicFenceMeshLayer extends CompositeLayer { @@ -141,6 +167,10 @@ export class SeismicFenceMeshLayer extends CompositeLayer= (256 * 256) - 1) { r = floor(float(vertexIndex) / (256.0 * 256.0)); diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts index 16329ae07..3d032aa03 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts @@ -1,5 +1,8 @@ import { CompositeLayer, CompositeLayerProps, FilterContext, Layer, UpdateParameters } from "@deck.gl/core"; -import { GeoJsonLayer, TextLayer } from "@deck.gl/layers"; +import { GeoJsonLayer } from "@deck.gl/layers"; +import { LabelOrganizer } from "@modules/3DViewerNew/view/utils/LabelOrganizer"; +import { ExtendedLayerProps } from "@webviz/subsurface-viewer"; +import { BoundingBox3D, ReportBoundingBoxAction } from "@webviz/subsurface-viewer/dist/components/Map"; import type { Feature, FeatureCollection } from "geojson"; @@ -17,10 +20,15 @@ type TextLayerData = { name: string; }; -export type WellBorePicksLayerProps = { +export interface WellBorePicksLayerProps extends ExtendedLayerProps { id: string; data: WellborePickLayerData[]; -}; + zIncreaseDownwards?: boolean; + + // Non public properties: + reportBoundingBox?: React.Dispatch; + reportLabels?: LabelOrganizer["registerLabels"]; +} export class WellborePicksLayer extends CompositeLayer { static layerName: string = "WellborePicksLayer"; @@ -35,13 +43,35 @@ export class WellborePicksLayer extends CompositeLayer return true; } + private calcBoundingBox(): BoundingBox3D { + const { data } = this.props; + + let minX = Number.MAX_VALUE; + let minY = Number.MAX_VALUE; + let minZ = Number.MAX_VALUE; + let maxX = Number.MIN_VALUE; + let maxY = Number.MIN_VALUE; + let maxZ = Number.MIN_VALUE; + + for (const wellPick of data) { + minX = Math.min(minX, wellPick.easting); + minY = Math.min(minY, wellPick.northing); + minZ = Math.min(minZ, wellPick.tvdMsl); + maxX = Math.max(maxX, wellPick.easting); + maxY = Math.max(maxY, wellPick.northing); + maxZ = Math.max(maxZ, wellPick.tvdMsl); + } + + return [minX, minY, minZ, maxX, maxY, maxZ]; + } + updateState(params: UpdateParameters>>): void { const features: Feature[] = params.props.data.map((wellPick) => { return { type: "Feature", geometry: { type: "Point", - coordinates: [wellPick.easting, wellPick.northing], + coordinates: [wellPick.easting, wellPick.northing, wellPick.tvdMsl], }, properties: { name: `${wellPick.wellBoreUwi}, TVD_MSL: ${wellPick.tvdMsl}, MD: ${wellPick.md}`, @@ -64,6 +94,10 @@ export class WellborePicksLayer extends CompositeLayer this._pointsData = pointsData; this._textData = textData; + + this.props.reportBoundingBox?.({ + layerBoundingBox: this.calcBoundingBox(), + }); } renderLayers() { @@ -71,6 +105,16 @@ export class WellborePicksLayer extends CompositeLayer const sizeMinPixels = 16; const sizeMaxPixels = 16; + this.props.reportLabels?.( + this.id, + this.props.data.map((wellPick) => { + return { + name: wellPick.wellBoreUwi, + referencePosition: [wellPick.easting, wellPick.northing, wellPick.tvdMsl], + }; + }) + ); + return [ new GeoJsonLayer( this.getSubLayerProps({ @@ -91,6 +135,7 @@ export class WellborePicksLayer extends CompositeLayer }) ), + /* new TextLayer( this.getSubLayerProps({ id: "text", @@ -114,8 +159,14 @@ export class WellborePicksLayer extends CompositeLayer getTextAnchor: "middle", getPosition: (d: TextLayerData) => d.coordinates, getText: (d: TextLayerData) => d.name, + extensions: [ + new CollisionFilterExtension({ + collisionEnabled: true, + }), + ], }) ), + */ ]; } } From 9cc9fb5b065739b988069ce45010cc2edbdb02f1 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 14 Feb 2025 00:43:19 +0100 Subject: [PATCH 53/97] wip --- .../view/components/InteractionWrapper.tsx | 188 ++++++++++++++++++ .../view/components/LayersWrapper.tsx | 4 +- .../view/components/ReadoutWrapper.tsx | 177 ++--------------- .../3DViewerNew/view/utils/LabelOrganizer.tsx | 47 +++-- 4 files changed, 235 insertions(+), 181 deletions(-) create mode 100644 frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx diff --git a/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx new file mode 100644 index 000000000..442e0fa56 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx @@ -0,0 +1,188 @@ +import React from "react"; + +import { Layer as DeckGlLayer, View as DeckGlView } from "@deck.gl/core"; +import { DeckGLRef } from "@deck.gl/react"; +import { useIntersectionPolylines } from "@framework/UserCreatedItems"; +import { IntersectionPolylinesEvent } from "@framework/userCreatedItems/IntersectionPolylines"; +import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; +import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; + +import { converter } from "culori"; + +import { ContextMenu } from "./ContextMenu"; +import { ReadooutWrapperProps, ReadoutWrapper } from "./ReadoutWrapper"; +import { Toolbar } from "./Toolbar"; + +import { DeckGlInstanceManager, DeckGlInstanceManagerTopic } from "../utils/DeckGlInstanceManager"; +import { LabelComponent, LabelOrganizer } from "../utils/LabelOrganizer"; +import { Polyline, PolylinesPlugin, PolylinesPluginTopic } from "../utils/PolylinesPlugin"; + +export type InteractionWrapperProps = {} & Omit< + ReadooutWrapperProps, + "deckGlManager" | "triggerHome" | "verticalScale" | "deckGlRef" +>; + +export function InteractionWrapper(props: InteractionWrapperProps): React.ReactNode { + const deckGlRef = React.useRef(null); + deckGlRef.current?.deck?.needsRedraw; + const [deckGlManager, setDeckGlManager] = React.useState( + new DeckGlInstanceManager(deckGlRef.current) + ); + const [labelOrganizer, setLabelOrganizer] = React.useState(new LabelOrganizer(deckGlRef.current)); + const [polylinesPlugin, setPolylinesPlugin] = React.useState(new PolylinesPlugin(deckGlManager)); + + usePublishSubscribeTopicValue(deckGlManager, DeckGlInstanceManagerTopic.REDRAW); + + const intersectionPolylines = useIntersectionPolylines(props.workbenchSession); + const colorSet = props.workbenchSettings.useColorSet(); + + const colorGenerator = React.useCallback( + function* colorGenerator() { + const colors: [number, number, number][] = colorSet.getColorArray().map((c) => { + const rgb = converter("rgb")(c); + if (!rgb) { + return [0, 0, 0]; + } + return [rgb.r * 255, rgb.g * 255, rgb.b * 255]; + }); + let i = 0; + while (true) { + yield colors[i % colors.length]; + i++; + } + }, + [colorSet] + ); + + React.useEffect( + function setupDeckGlManager() { + const manager = new DeckGlInstanceManager(deckGlRef.current); + setDeckGlManager(manager); + + labelOrganizer.setDeckRef(deckGlRef.current); + + const polylinesPlugin = new PolylinesPlugin(manager, colorGenerator()); + polylinesPlugin.setPolylines(intersectionPolylines.getPolylines()); + manager.addPlugin(polylinesPlugin); + setPolylinesPlugin(polylinesPlugin); + + const unsubscribeFromPolylinesPlugin = polylinesPlugin + .getPublishSubscribeDelegate() + .makeSubscriberFunction(PolylinesPluginTopic.EDITING_POLYLINE_ID)(() => { + if (polylinesPlugin.getCurrentEditingPolylineId() === null) { + intersectionPolylines.setPolylines(polylinesPlugin.getPolylines()); + } + }); + + const unsubscribeFromIntersectionPolylines = intersectionPolylines.subscribe( + IntersectionPolylinesEvent.CHANGE, + () => { + polylinesPlugin.setPolylines(intersectionPolylines.getPolylines()); + } + ); + + return function cleanupDeckGlManager() { + manager.beforeDestroy(); + unsubscribeFromPolylinesPlugin(); + unsubscribeFromIntersectionPolylines(); + }; + }, + [intersectionPolylines, colorGenerator, labelOrganizer] + ); + + const [triggerHomeCounter, setTriggerHomeCounter] = React.useState(0); + const [gridVisible, setGridVisible] = React.useState(false); + const [verticalScale, setVerticalScale] = React.useState(1); + const [polylines, setPolylines] = React.useState([]); + + function handleFitInViewClick() { + setTriggerHomeCounter((prev) => prev + 1); + } + + function handleGridVisibilityChange(visible: boolean) { + setGridVisible(visible); + } + + function handleVerticalScaleChange(value: number) { + setVerticalScale(value); + } + + const activePolylineId = polylinesPlugin.getCurrentEditingPolylineId(); + + const handlePolylineNameChange = React.useCallback( + function handlePolylineNameChange(name: string): void { + if (!activePolylineId) { + return; + } + + setPolylines((prev) => + prev.map((polyline) => { + if (polyline.id === activePolylineId) { + return { + ...polyline, + name, + }; + } + + return polyline; + }) + ); + }, + [activePolylineId] + ); + + let adjustedLayers: DeckGlLayer[] = []; + for (const layer of props.layers) { + adjustedLayers.push( + layer.clone({ + // @ts-expect-error - we need to add the registerLabels function to the layer + reportLabels: labelOrganizer.registerLabels.bind(labelOrganizer), + }) + ); + } + if (!gridVisible) { + adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); + } + + const viewportLabels = labelOrganizer.makeLabelComponents(); + const viewportAnnotations = viewportLabels.map((el) => { + return ( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + /* @ts-expect-error */ + +
+ {el.labels.map((label) => ( + + ))} +
+
+ ); + }); + + const adjustedViewportAnnotations = [...props.viewportAnnotations, ...viewportAnnotations]; + + return ( + <> + p.id === activePolylineId)?.name} + /> + + + + ); +} diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 85ff7ecb9..cb0f6eb8f 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -40,7 +40,7 @@ import { BoundingBox3D, ViewportType } from "@webviz/subsurface-viewer"; import { ViewsType } from "@webviz/subsurface-viewer/dist/SubsurfaceViewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; -import { ReadoutWrapper } from "./ReadoutWrapper"; +import { InteractionWrapper } from "./InteractionWrapper"; import { PlaceholderLayer } from "../../../_shared/customDeckGlLayers/PlaceholderLayer"; @@ -202,7 +202,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { return (
- ; }; export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { const id = React.useId(); const deckGlRef = React.useRef(null); - deckGlRef.current?.deck?.needsRedraw; - const [deckGlManager, setDeckGlManager] = React.useState( - new DeckGlInstanceManager(deckGlRef.current) - ); - const [labelOrganizer, setLabelOrganizer] = React.useState(new LabelOrganizer(deckGlRef.current)); - const [polylinesPlugin, setPolylinesPlugin] = React.useState(new PolylinesPlugin(deckGlManager)); - - usePublishSubscribeTopicValue(deckGlManager, DeckGlInstanceManagerTopic.REDRAW); - - const intersectionPolylines = useIntersectionPolylines(props.workbenchSession); - const colorSet = props.workbenchSettings.useColorSet(); - - const colorGenerator = React.useCallback( - function* colorGenerator() { - const colors: [number, number, number][] = colorSet.getColorArray().map((c) => { - const rgb = converter("rgb")(c); - if (!rgb) { - return [0, 0, 0]; - } - return [rgb.r * 255, rgb.g * 255, rgb.b * 255]; - }); - let i = 0; - while (true) { - yield colors[i % colors.length]; - i++; - } - }, - [colorSet] - ); - - React.useEffect( - function setupDeckGlManager() { - const manager = new DeckGlInstanceManager(deckGlRef.current); - setDeckGlManager(manager); - labelOrganizer.setDeckRef(deckGlRef.current); + React.useImperativeHandle(props.deckGlRef, () => deckGlRef.current); - const polylinesPlugin = new PolylinesPlugin(manager, colorGenerator()); - polylinesPlugin.setPolylines(intersectionPolylines.getPolylines()); - manager.addPlugin(polylinesPlugin); - setPolylinesPlugin(polylinesPlugin); - - const unsubscribeFromPolylinesPlugin = polylinesPlugin - .getPublishSubscribeDelegate() - .makeSubscriberFunction(PolylinesPluginTopic.EDITING_POLYLINE_ID)(() => { - if (polylinesPlugin.getCurrentEditingPolylineId() === null) { - intersectionPolylines.setPolylines(polylinesPlugin.getPolylines()); - } - }); - - const unsubscribeFromIntersectionPolylines = intersectionPolylines.subscribe( - IntersectionPolylinesEvent.CHANGE, - () => { - polylinesPlugin.setPolylines(intersectionPolylines.getPolylines()); - } - ); - - return function cleanupDeckGlManager() { - manager.beforeDestroy(); - unsubscribeFromPolylinesPlugin(); - unsubscribeFromIntersectionPolylines(); - }; - }, - [intersectionPolylines, colorGenerator, labelOrganizer] - ); - - const [cameraPositionSetByAction, setCameraPositionSetByAction] = React.useState(null); - const [triggerHomeCounter, setTriggerHomeCounter] = React.useState(0); const [layerPickingInfo, setLayerPickingInfo] = React.useState([]); - const [gridVisible, setGridVisible] = React.useState(false); - const [verticalScale, setVerticalScale] = React.useState(1); - const [polylines, setPolylines] = React.useState([]); - - function handleFitInViewClick() { - setTriggerHomeCounter((prev) => prev + 1); - } - - function handleGridVisibilityChange(visible: boolean) { - setGridVisible(visible); - } - - function handleVerticalScaleChange(value: number) { - setVerticalScale(value); - } - - const activePolylineId = polylinesPlugin.getCurrentEditingPolylineId(); - - const handlePolylineNameChange = React.useCallback( - function handlePolylineNameChange(name: string): void { - if (!activePolylineId) { - return; - } - - setPolylines((prev) => - prev.map((polyline) => { - if (polyline.id === activePolylineId) { - return { - ...polyline, - name, - }; - } - - return polyline; - }) - ); - }, - [activePolylineId] - ); function handleMouseEvent(event: MapMouseEvent) { const pickingInfo = event.infos; setLayerPickingInfo(pickingInfo); } - let adjustedLayers: DeckGlLayer[] = []; - for (const layer of props.layers) { - adjustedLayers.push( - layer.clone({ - // @ts-expect-error - we need to add the registerLabels function to the layer - reportLabels: labelOrganizer.registerLabels.bind(labelOrganizer), - }) - ); - } - if (!gridVisible) { - adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); - } - - const viewportLabels = labelOrganizer.makeLabelComponents(); - const viewportAnnotations = viewportLabels.map((el) => { - return ( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - /* @ts-expect-error */ - -
- {el.labels.map((label) => ( - - ))} -
-
- ); - }); - return ( <> - p.id === activePolylineId)?.name} - /> - setCameraPositionSetByAction(null), - verticalScale, + verticalScale: props.verticalScale, scale: { visible: true, incrementValue: 100, @@ -212,14 +60,13 @@ export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { multiPicking: true, pickDepth: 2, }, - triggerHome: triggerHomeCounter, + triggerHome: props.triggerHome, pickingRadius: 5, - layers: adjustedLayers, + layers: props.layers, onMouseEvent: handleMouseEvent, })} > {props.viewportAnnotations} - {viewportAnnotations} {props.views.viewports.length === 0 && (
diff --git a/frontend/src/modules/3DViewerNew/view/utils/LabelOrganizer.tsx b/frontend/src/modules/3DViewerNew/view/utils/LabelOrganizer.tsx index 0f2e86998..33f480068 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/LabelOrganizer.tsx +++ b/frontend/src/modules/3DViewerNew/view/utils/LabelOrganizer.tsx @@ -11,7 +11,7 @@ export type Label = { export class LabelOrganizer { private _labelsMap: Map = new Map(); private _animationFrameId: number | null = null; - private _subscribers: (() => void)[] = []; + private _subscribers: ((project: (pos: [number, number, number]) => Vec2) => void)[] = []; private _ref: DeckGLRef | null; @@ -21,18 +21,37 @@ export class LabelOrganizer { setDeckRef(ref: DeckGLRef | null) { this._ref = ref; - if (this._ref?.deck) { - this._ref.deck.props.onViewStateChange = () => { - this.maybeUpdateLabels(); - }; - } + this.maybeUpdateLabels(); } private maybeUpdateLabels() { - this._subscribers.forEach((s) => s()); + if (this._animationFrameId) { + cancelAnimationFrame(this._animationFrameId); + } + + this._animationFrameId = requestAnimationFrame(() => { + this._animationFrameId = null; + this.maybeUpdateLabels(); + }); + + if (!this._ref?.deck?.isInitialized) { + return; + } + const viewport = this._ref?.deck?.getViewports()[0]; + if (!viewport) { + return; + } + const project = (position: [number, number, number]) => { + const pos = viewport.project(position); + return { + x: pos[0], + y: pos[1], + }; + }; + this._subscribers.forEach((s) => s(project)); } - subscribe(func: () => void) { + subscribe(func: (project: (pos: [number, number, number]) => Vec2) => void) { this._subscribers.push(func); } @@ -52,7 +71,6 @@ export class LabelOrganizer { for (const [layerId, labels] of this._labelsMap) { for (const viewport of viewports) { - const projectFunc = viewport.project; const viewportId = viewport.id; const viewportLabels: LabelComponentProps[] = labels.map((label) => { return { @@ -61,7 +79,7 @@ export class LabelOrganizer { referencePosition: label.referencePosition, position: label.referencePosition, projectFunc: (position: [number, number, number]) => { - const pos = projectFunc(position); + const pos = viewport.project(position); return { x: pos[0], y: pos[1], @@ -94,18 +112,19 @@ type LabelComponentProps = { referencePosition: [number, number, number]; position: [number, number, number]; projectFunc: (position: [number, number, number]) => Vec2; - subscribeToViewportChange: (func: () => void) => void; + subscribeToViewportChange: (func: (projectionFunc: (position: [number, number, number]) => Vec2) => void) => void; }; export function LabelComponent(props: LabelComponentProps) { + const { projectFunc, subscribeToViewportChange } = props; const [position, setPosition] = React.useState(props.projectFunc(props.referencePosition)); React.useEffect(() => { - props.subscribeToViewportChange(() => { - const newPosition = props.projectFunc(props.referencePosition); + props.subscribeToViewportChange((project) => { + const newPosition = project(props.referencePosition); setPosition(newPosition); }); - }, [props.projectFunc, props.referencePosition, props.subscribeToViewportChange]); + }, [props.referencePosition, subscribeToViewportChange]); return (
Date: Tue, 18 Feb 2025 17:09:57 +0100 Subject: [PATCH 54/97] wip --- .../services/vds_access/response_types.py | 12 +- .../RealizationSeismicCrosslineLayer.ts | 4 +- .../RealizationSeismicDepthSliceLayer.ts | 2 +- .../view/components/InteractionWrapper.tsx | 35 +---- .../3DViewerNew/view/utils/LabelOrganizer.tsx | 144 ------------------ .../customDeckGlLayers/WellborePicksLayer.ts | 12 -- 6 files changed, 10 insertions(+), 199 deletions(-) delete mode 100644 frontend/src/modules/3DViewerNew/view/utils/LabelOrganizer.tsx diff --git a/backend_py/primary/primary/services/vds_access/response_types.py b/backend_py/primary/primary/services/vds_access/response_types.py index dec4613c2..2e85919d2 100644 --- a/backend_py/primary/primary/services/vds_access/response_types.py +++ b/backend_py/primary/primary/services/vds_access/response_types.py @@ -48,20 +48,16 @@ class VdsArray: format: str shape: List[int] + class VdsAxis(BaseModel): """ Definition of an axis from vds-slice -class VdsAxis(BaseModel): - """ - Definition of a fence metadata response from vds-slice + Neglected: + - stepsize: float - See: https://github.com/equinor/vds-slice/blob/ab6f39789bf3d3b59a8df14f1c4682d340dc0bf3/internal/core/core.go#L160-L162 + See: https://github.com/equinor/vds-slice/blob/ab6f39789bf3d3b59a8df14f1c4682d340dc0bf3/internal/core/core.go#L37-L55 """ - x: VdsAxis - y: VdsAxis - geospatial: List[List[float]] - # shape: List[int] annotation: VdsDirection max: float diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index 719328501..4672a45f4 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -100,8 +100,8 @@ export class RealizationSeismicCrosslineLayer const minXY = { x: xmin, y: ymin }; const origin = { x: meta.spec.xOrigin, y: meta.spec.yOrigin }; - const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotation / 180.0) * Math.PI); - const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotation / 180.0) * Math.PI); + const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); + const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); return { x: [rotatedMinXY.x, rotatedMaxXY.x], y: [rotatedMinXY.y, rotatedMaxXY.y], z: [zmin, zmax] }; } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts index 221ce6cbe..70d3b1faf 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts @@ -105,7 +105,7 @@ export class RealizationSeismicDepthSliceLayer seismic_attribute: seismicAttribute ?? "", time_or_interval_str: timeOrInterval ?? "", observed: false, - depth: seismicDepth ?? 0, + depth_slice_no: seismicDepth ?? 0, }, }), }) diff --git a/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx index 442e0fa56..2cf9f8de1 100644 --- a/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { Layer as DeckGlLayer, View as DeckGlView } from "@deck.gl/core"; +import { Layer as DeckGlLayer } from "@deck.gl/core"; import { DeckGLRef } from "@deck.gl/react"; import { useIntersectionPolylines } from "@framework/UserCreatedItems"; import { IntersectionPolylinesEvent } from "@framework/userCreatedItems/IntersectionPolylines"; @@ -14,7 +14,6 @@ import { ReadooutWrapperProps, ReadoutWrapper } from "./ReadoutWrapper"; import { Toolbar } from "./Toolbar"; import { DeckGlInstanceManager, DeckGlInstanceManagerTopic } from "../utils/DeckGlInstanceManager"; -import { LabelComponent, LabelOrganizer } from "../utils/LabelOrganizer"; import { Polyline, PolylinesPlugin, PolylinesPluginTopic } from "../utils/PolylinesPlugin"; export type InteractionWrapperProps = {} & Omit< @@ -28,7 +27,6 @@ export function InteractionWrapper(props: InteractionWrapperProps): React.ReactN const [deckGlManager, setDeckGlManager] = React.useState( new DeckGlInstanceManager(deckGlRef.current) ); - const [labelOrganizer, setLabelOrganizer] = React.useState(new LabelOrganizer(deckGlRef.current)); const [polylinesPlugin, setPolylinesPlugin] = React.useState(new PolylinesPlugin(deckGlManager)); usePublishSubscribeTopicValue(deckGlManager, DeckGlInstanceManagerTopic.REDRAW); @@ -59,8 +57,6 @@ export function InteractionWrapper(props: InteractionWrapperProps): React.ReactN const manager = new DeckGlInstanceManager(deckGlRef.current); setDeckGlManager(manager); - labelOrganizer.setDeckRef(deckGlRef.current); - const polylinesPlugin = new PolylinesPlugin(manager, colorGenerator()); polylinesPlugin.setPolylines(intersectionPolylines.getPolylines()); manager.addPlugin(polylinesPlugin); @@ -87,7 +83,7 @@ export function InteractionWrapper(props: InteractionWrapperProps): React.ReactN unsubscribeFromIntersectionPolylines(); }; }, - [intersectionPolylines, colorGenerator, labelOrganizer] + [intersectionPolylines, colorGenerator] ); const [triggerHomeCounter, setTriggerHomeCounter] = React.useState(0); @@ -132,35 +128,10 @@ export function InteractionWrapper(props: InteractionWrapperProps): React.ReactN ); let adjustedLayers: DeckGlLayer[] = []; - for (const layer of props.layers) { - adjustedLayers.push( - layer.clone({ - // @ts-expect-error - we need to add the registerLabels function to the layer - reportLabels: labelOrganizer.registerLabels.bind(labelOrganizer), - }) - ); - } if (!gridVisible) { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); } - const viewportLabels = labelOrganizer.makeLabelComponents(); - const viewportAnnotations = viewportLabels.map((el) => { - return ( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - /* @ts-expect-error */ - -
- {el.labels.map((label) => ( - - ))} -
-
- ); - }); - - const adjustedViewportAnnotations = [...props.viewportAnnotations, ...viewportAnnotations]; - return ( <> = new Map(); - private _animationFrameId: number | null = null; - private _subscribers: ((project: (pos: [number, number, number]) => Vec2) => void)[] = []; - - private _ref: DeckGLRef | null; - - constructor(ref: DeckGLRef | null) { - this._ref = ref; - } - - setDeckRef(ref: DeckGLRef | null) { - this._ref = ref; - this.maybeUpdateLabels(); - } - - private maybeUpdateLabels() { - if (this._animationFrameId) { - cancelAnimationFrame(this._animationFrameId); - } - - this._animationFrameId = requestAnimationFrame(() => { - this._animationFrameId = null; - this.maybeUpdateLabels(); - }); - - if (!this._ref?.deck?.isInitialized) { - return; - } - const viewport = this._ref?.deck?.getViewports()[0]; - if (!viewport) { - return; - } - const project = (position: [number, number, number]) => { - const pos = viewport.project(position); - return { - x: pos[0], - y: pos[1], - }; - }; - this._subscribers.forEach((s) => s(project)); - } - - subscribe(func: (project: (pos: [number, number, number]) => Vec2) => void) { - this._subscribers.push(func); - } - - registerLabels(layerId: string, labels: Label[]) { - this._labelsMap.set(layerId, labels); - } - - makeLabelComponents(): { viewportId: string; labels: LabelComponentProps[] }[] { - const viewports = this._ref?.deck?.getViewports(); - if (!viewports) { - return []; - } - - this._subscribers = []; - - const result: { viewportId: string; labels: LabelComponentProps[] }[] = []; - - for (const [layerId, labels] of this._labelsMap) { - for (const viewport of viewports) { - const viewportId = viewport.id; - const viewportLabels: LabelComponentProps[] = labels.map((label) => { - return { - id: label.name, - name: label.name, - referencePosition: label.referencePosition, - position: label.referencePosition, - projectFunc: (position: [number, number, number]) => { - const pos = viewport.project(position); - return { - x: pos[0], - y: pos[1], - }; - }, - subscribeToViewportChange: this.subscribe.bind(this), - }; - }); - - const existing = result.find((r) => r.viewportId === viewportId); - if (existing) { - existing.labels.push(...viewportLabels); - continue; - } - - result.push({ - viewportId, - labels: viewportLabels, - }); - } - } - - return result; - } -} - -type LabelComponentProps = { - id: string; - name: string; - referencePosition: [number, number, number]; - position: [number, number, number]; - projectFunc: (position: [number, number, number]) => Vec2; - subscribeToViewportChange: (func: (projectionFunc: (position: [number, number, number]) => Vec2) => void) => void; -}; - -export function LabelComponent(props: LabelComponentProps) { - const { projectFunc, subscribeToViewportChange } = props; - const [position, setPosition] = React.useState(props.projectFunc(props.referencePosition)); - - React.useEffect(() => { - props.subscribeToViewportChange((project) => { - const newPosition = project(props.referencePosition); - setPosition(newPosition); - }); - }, [props.referencePosition, subscribeToViewportChange]); - - return ( -
- {props.name} -
- ); -} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts index 3d032aa03..6f09a63d8 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts @@ -1,6 +1,5 @@ import { CompositeLayer, CompositeLayerProps, FilterContext, Layer, UpdateParameters } from "@deck.gl/core"; import { GeoJsonLayer } from "@deck.gl/layers"; -import { LabelOrganizer } from "@modules/3DViewerNew/view/utils/LabelOrganizer"; import { ExtendedLayerProps } from "@webviz/subsurface-viewer"; import { BoundingBox3D, ReportBoundingBoxAction } from "@webviz/subsurface-viewer/dist/components/Map"; @@ -27,7 +26,6 @@ export interface WellBorePicksLayerProps extends ExtendedLayerProps { // Non public properties: reportBoundingBox?: React.Dispatch; - reportLabels?: LabelOrganizer["registerLabels"]; } export class WellborePicksLayer extends CompositeLayer { @@ -105,16 +103,6 @@ export class WellborePicksLayer extends CompositeLayer const sizeMinPixels = 16; const sizeMaxPixels = 16; - this.props.reportLabels?.( - this.id, - this.props.data.map((wellPick) => { - return { - name: wellPick.wellBoreUwi, - referencePosition: [wellPick.easting, wellPick.northing, wellPick.tvdMsl], - }; - }) - ); - return [ new GeoJsonLayer( this.getSubLayerProps({ From 316135f9a585cbb80a32835c9e2b87135e5a2216 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 19 Feb 2025 15:31:29 +0100 Subject: [PATCH 55/97] wip --- frontend/src/lib/utils/boundingBox.ts | 68 ++++++++ frontend/src/lib/utils/mat3.ts | 55 +++++++ frontend/src/lib/utils/orientedBoundingBox.ts | 145 ++++++++++++++++++ frontend/src/lib/utils/vec3.ts | 72 +++++++++ .../2DViewer/view/utils/makeViewsAndLayers.ts | 2 +- .../RealizationSeismicCrosslineLayer.ts | 32 ++-- .../RealizationSeismicDepthSliceLayer.ts | 78 ++++++++-- ...izationSeismicDepthSliceSettingsContext.ts | 42 +++-- .../types.ts | 5 + .../makeSeismicFenceMeshLayer.ts | 138 +++-------------- .../view/components/InteractionWrapper.tsx | 4 +- .../view/components/LayersWrapper.tsx | 4 +- .../LayerFramework/delegates/LayerDelegate.ts | 37 ++--- .../_shared/LayerFramework/interfaces.ts | 11 +- .../DrilledWellTrajectoriesLayer.ts | 27 ++-- .../DrilledWellborePicksLayer.ts | 31 ++-- .../visualization/VisualizationFactory.ts | 25 ++- ...icksLayer.ts => makeWellborePicksLayer.ts} | 0 .../{wellsLayer.ts => makeWellsLayer.ts} | 10 +- .../customDeckGlLayers/AdvancedWellsLayer.ts | 77 +++------- .../SeismicFenceMeshLayer.ts | 1 - .../SeismicFenceMeshLayer/_private/worker.ts | 19 ++- 22 files changed, 591 insertions(+), 292 deletions(-) create mode 100644 frontend/src/lib/utils/boundingBox.ts create mode 100644 frontend/src/lib/utils/mat3.ts create mode 100644 frontend/src/lib/utils/orientedBoundingBox.ts create mode 100644 frontend/src/lib/utils/vec3.ts rename frontend/src/modules/_shared/LayerFramework/visualization/deckgl/{wellborePicksLayer.ts => makeWellborePicksLayer.ts} (100%) rename frontend/src/modules/_shared/LayerFramework/visualization/deckgl/{wellsLayer.ts => makeWellsLayer.ts} (88%) diff --git a/frontend/src/lib/utils/boundingBox.ts b/frontend/src/lib/utils/boundingBox.ts new file mode 100644 index 000000000..bbc2fdaf1 --- /dev/null +++ b/frontend/src/lib/utils/boundingBox.ts @@ -0,0 +1,68 @@ +import { BoundingBox3D_api } from "@api"; + +import * as vec3 from "./vec3"; + +export type BBox = { + min: vec3.Vec3; + max: vec3.Vec3; +}; + +/* + Creates a new bounding box. + */ +export function create(min: vec3.Vec3, max: vec3.Vec3): BBox { + return { min, max }; +} + +export function fromBoundingBox3DApi(boundingBox: BoundingBox3D_api): BBox { + return create( + vec3.create(boundingBox.xmin, boundingBox.ymin, boundingBox.zmin), + vec3.create(boundingBox.xmax, boundingBox.ymax, boundingBox.zmax) + ); +} + +/* + Returns true if the bounding box contains the given point. + */ +export function containsPoint(box: BBox, point: vec3.Vec3): boolean { + return ( + point.x >= box.min.x && + point.x <= box.max.x && + point.y >= box.min.y && + point.y <= box.max.y && + point.z >= box.min.z && + point.z <= box.max.z + ); +} + +/* + Returns true if the two bounding boxes intersect. + */ +export function intersects(box1: BBox, box2: BBox): boolean { + return ( + box1.min.x <= box2.max.x && + box1.max.x >= box2.min.x && + box1.min.y <= box2.max.y && + box1.max.y >= box2.min.y && + box1.min.z <= box2.max.z && + box1.max.z >= box2.min.z + ); +} + +/* + Converts a bounding box to an array of numbers. + The array contains the following numbers in the following order: + [min.x, min.y, min.z, max.x, max.y, max.z] + */ +export function toNumArray(box: BBox): [number, number, number, number, number, number] { + return [box.min.x, box.min.y, box.min.z, box.max.x, box.max.y, box.max.z]; +} + +/* + Converts an array of numbers to a bounding box. + The array should contain the following numbers in the following order: + [min.x, min.y, min.z, max.x, max.y, max.z] + */ +export function fromNumArray(array: [number, number, number, number, number, number]): BBox { + return create(vec3.fromArray(array.slice(0, 3)), vec3.fromArray(array.slice(3, 6))); +} diff --git a/frontend/src/lib/utils/mat3.ts b/frontend/src/lib/utils/mat3.ts new file mode 100644 index 000000000..699fde847 --- /dev/null +++ b/frontend/src/lib/utils/mat3.ts @@ -0,0 +1,55 @@ +export type Mat3 = { + m00: number; + m01: number; + m02: number; + m10: number; + m11: number; + m12: number; + m20: number; + m21: number; + m22: number; +}; + +export function createEmpty(): Mat3 { + return { + m00: 0, + m01: 0, + m02: 0, + m10: 0, + m11: 0, + m12: 0, + m20: 0, + m21: 0, + m22: 0, + }; +} + +export function create( + m00: number, + m01: number, + m02: number, + m10: number, + m11: number, + m12: number, + m20: number, + m21: number, + m22: number +): Mat3 { + return { m00, m01, m02, m10, m11, m12, m20, m21, m22 }; +} + +export function fromArray( + array: ArrayLike | [number, number, number, number, number, number, number, number, number] +): Mat3 { + return { + m00: array[0], + m01: array[1], + m02: array[2], + m10: array[3], + m11: array[4], + m12: array[5], + m20: array[6], + m21: array[7], + m22: array[8], + }; +} diff --git a/frontend/src/lib/utils/orientedBoundingBox.ts b/frontend/src/lib/utils/orientedBoundingBox.ts new file mode 100644 index 000000000..ea6823c56 --- /dev/null +++ b/frontend/src/lib/utils/orientedBoundingBox.ts @@ -0,0 +1,145 @@ +import * as bbox from "./boundingBox"; +import * as vec3 from "./vec3"; + +export type OBBox = { + centerPoint: vec3.Vec3; + principalAxes: vec3.Vec3[]; + halfExtents: number[]; +}; + +/* + Creates a new oriented bounding box. + */ +export function create(center: vec3.Vec3, principalAxes: vec3.Vec3[], halfExtents: number[]): OBBox { + return { centerPoint: center, principalAxes, halfExtents }; +} + +/* + Returns true if the oriented bounding box contains the given point. + */ +export function containsPoint(box: OBBox, point: vec3.Vec3): boolean { + const diff = vec3.subtract(point, box.centerPoint); + return ( + Math.abs(vec3.dot(diff, box.principalAxes[0])) <= box.halfExtents[0] && + Math.abs(vec3.dot(diff, box.principalAxes[1])) <= box.halfExtents[1] && + Math.abs(vec3.dot(diff, box.principalAxes[2])) <= box.halfExtents[2] + ); +} + +/* + Converts an oriented bounding box to an axis-aligned bounding box. + */ +export function toAxisAlignedBoundingBox(box: OBBox): bbox.BBox { + const absAxisX = vec3.abs(box.principalAxes[0]); + const absAxisY = vec3.abs(box.principalAxes[1]); + const absAxisZ = vec3.abs(box.principalAxes[2]); + + const halfSize: vec3.Vec3 = { + x: box.halfExtents[0] * absAxisX.x + box.halfExtents[1] * absAxisY.x + box.halfExtents[2] * absAxisZ.x, + y: box.halfExtents[0] * absAxisX.y + box.halfExtents[1] * absAxisY.y + box.halfExtents[2] * absAxisZ.y, + z: box.halfExtents[0] * absAxisX.z + box.halfExtents[1] * absAxisY.z + box.halfExtents[2] * absAxisZ.z, + }; + return bbox.create(vec3.subtract(box.centerPoint, halfSize), vec3.add(box.centerPoint, halfSize)); +} + +export function fromAxisAlignedBoundingBox(box: bbox.BBox): OBBox { + const centerPoint = vec3.scale(vec3.add(box.min, box.max), 0.5); + const principalAxes = [vec3.create(1, 0, 0), vec3.create(0, 1, 0), vec3.create(0, 0, 1)]; + const halfExtents = vec3.scale(vec3.subtract(box.max, box.min), 0.5); + return create(centerPoint, principalAxes, [halfExtents.x, halfExtents.y, halfExtents.z]); +} + +/* + Returns the corner points of the oriented bounding box. + + The points are returned in the following order for z-axis up coordinate system: + 0: bottom front left + 1: bottom front right + 2: bottom back left + 3: bottom back right + 4: top front left + 5: top front right + 6: top back left + 7: top back right + */ + +export function calcCornerPoints(box: OBBox): vec3.Vec3[] { + const halfExtents = box.halfExtents; + const principalAxes = box.principalAxes; + const centerPoint = box.centerPoint; + + const points: vec3.Vec3[] = []; + for (let i = 0; i < 8; i++) { + const x = (i & 1) === 0 ? -1 : 1; + const y = (i & 2) === 0 ? -1 : 1; + const z = (i & 4) === 0 ? -1 : 1; + const point = vec3.add( + centerPoint, + vec3.add( + vec3.scale(principalAxes[0], x * halfExtents[0]), + vec3.add( + vec3.scale(principalAxes[1], y * halfExtents[1]), + vec3.scale(principalAxes[2], z * halfExtents[2]) + ) + ) + ); + points.push(point); + } + return points; +} + +export function fromCornerPoints(points: vec3.Vec3[]): OBBox { + const min = vec3.create(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); + const max = vec3.create(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); + for (const point of points) { + min.x = Math.min(min.x, point.x); + min.y = Math.min(min.y, point.y); + min.z = Math.min(min.z, point.z); + max.x = Math.max(max.x, point.x); + max.y = Math.max(max.y, point.y); + max.z = Math.max(max.z, point.z); + } + const center = vec3.scale(vec3.add(min, max), 0.5); + const halfExtents = vec3.scale(vec3.subtract(max, min), 0.5); + const principalAxes = [ + vec3.normalize(vec3.subtract(points[0], points[1])), + vec3.normalize(vec3.subtract(points[0], points[2])), + vec3.normalize(vec3.subtract(points[0], points[4])), + ]; + return create(center, principalAxes, [halfExtents.x, halfExtents.y, halfExtents.z]); +} + +/* + Combines two oriented bounding boxes into a new oriented bounding box that contains both input boxes. + */ +export function combine(box1: OBBox, box2: OBBox): OBBox { + const points1 = calcCornerPoints(box1); + const points2 = calcCornerPoints(box2); + const allPoints = points1.concat(points2); + const min = vec3.create(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); + const max = vec3.create(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); + for (const point of allPoints) { + min.x = Math.min(min.x, point.x); + min.y = Math.min(min.y, point.y); + min.z = Math.min(min.z, point.z); + max.x = Math.max(max.x, point.x); + max.y = Math.max(max.y, point.y); + max.z = Math.max(max.z, point.z); + } + const center = vec3.scale(vec3.add(min, max), 0.5); + const halfExtents = vec3.scale(vec3.subtract(max, min), 0.5); + const principalAxes = [ + vec3.normalize(vec3.subtract(points1[0], points1[1])), + vec3.normalize(vec3.subtract(points1[0], points1[2])), + vec3.normalize(vec3.subtract(points1[0], points1[4])), + ]; + return create(center, principalAxes, [halfExtents.x, halfExtents.y, halfExtents.z]); +} + +export function clone(box: OBBox): OBBox { + return create( + vec3.clone(box.centerPoint), + [vec3.clone(box.principalAxes[0]), vec3.clone(box.principalAxes[1]), vec3.clone(box.principalAxes[2])], + [...box.halfExtents] + ); +} diff --git a/frontend/src/lib/utils/vec3.ts b/frontend/src/lib/utils/vec3.ts new file mode 100644 index 000000000..d1a07d545 --- /dev/null +++ b/frontend/src/lib/utils/vec3.ts @@ -0,0 +1,72 @@ +import { Mat3 } from "./mat3"; + +export type Vec3 = { + x: number; + y: number; + z: number; +}; + +export function create(x: number, y: number, z: number): Vec3 { + return { x, y, z }; +} + +export function fromArray(array: ArrayLike | [number, number, number]): Vec3 { + return { x: array[0], y: array[1], z: array[2] }; +} + +export function length(vector: Vec3): number { + return Math.sqrt(vector.x ** 2 + vector.y ** 2 + vector.z ** 2); +} + +export function distance(point1: Vec3, point2: Vec3): number { + return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2 + (point1.z - point2.z) ** 2); +} + +export function subtract(minuend: Vec3, subtrahend: Vec3): Vec3 { + return { x: minuend.x - subtrahend.x, y: minuend.y - subtrahend.y, z: minuend.z - subtrahend.z }; +} + +export function add(vector1: Vec3, vector2: Vec3): Vec3 { + return { x: vector1.x + vector2.x, y: vector1.y + vector2.y, z: vector1.z + vector2.z }; +} + +export function normalize(vector: Vec3): Vec3 { + const len = length(vector); + return { x: vector.x / len, y: vector.y / len, z: vector.z / len }; +} + +export function abs(vector: Vec3): Vec3 { + return { x: Math.abs(vector.x), y: Math.abs(vector.y), z: Math.abs(vector.z) }; +} + +export function scale(vector: Vec3, scalar: number): Vec3 { + return { x: vector.x * scalar, y: vector.y * scalar, z: vector.z * scalar }; +} + +export function equal(vector1: Vec3, vector2: Vec3): boolean { + return vector1.x === vector2.x && vector1.y === vector2.y && vector1.z === vector2.z; +} + +export function dot(vector1: Vec3, vector2: Vec3): number { + return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z; +} + +export function cross(vector1: Vec3, vector2: Vec3): Vec3 { + return { + x: vector1.y * vector2.z - vector1.z * vector2.y, + y: vector1.z * vector2.x - vector1.x * vector2.z, + z: vector1.x * vector2.y - vector1.y * vector2.x, + }; +} + +export function transform(vector: Vec3, matrix: Mat3): Vec3 { + return { + x: matrix.m00 * vector.x + matrix.m01 * vector.y + matrix.m02 * vector.z, + y: matrix.m10 * vector.x + matrix.m11 * vector.y + matrix.m12 * vector.z, + z: matrix.m20 * vector.x + matrix.m21 * vector.y + matrix.m22 * vector.z, + }; +} + +export function clone(vector: Vec3): Vec3 { + return { x: vector.x, y: vector.y, z: vector.z }; +} diff --git a/frontend/src/modules/2DViewer/view/utils/makeViewsAndLayers.ts b/frontend/src/modules/2DViewer/view/utils/makeViewsAndLayers.ts index 824c59cea..285f1f911 100644 --- a/frontend/src/modules/2DViewer/view/utils/makeViewsAndLayers.ts +++ b/frontend/src/modules/2DViewer/view/utils/makeViewsAndLayers.ts @@ -112,7 +112,7 @@ export function recursivelyMakeViewsAndLayers( collectedColorScales.push(colorScale); } - const boundingBox = child.getLayerDelegate().getBoundingBox(); + const boundingBox = child.getLayerDelegate().getOrientedBoundingBox(); maybeApplyBoundingBox(boundingBox); collectedLayers.push({ layer, position: numCollectedLayers + collectedLayers.length }); } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index 4672a45f4..475ad42f2 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -1,9 +1,11 @@ import { getCrosslineSliceOptions } from "@api"; +import { OBBox, fromCornerPoints } from "@lib/utils/orientedBoundingBox"; import { rotatePoint2Around } from "@lib/utils/vec2"; +import { Vec3 } from "@lib/utils/vec3"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { QueryClient } from "@tanstack/react-query"; @@ -55,27 +57,26 @@ export class RealizationSeismicCrosslineLayer return !isEqual(prevSettings, newSettings); } - makeBoundingBox(): BoundingBox | null { + makeBoundingBox(): OBBox | null { const data = this._layerDelegate.getData(); if (!data) { return null; } - return { - x: [data.bbox_utm[0][0], data.bbox_utm[1][0]], - y: [data.bbox_utm[0][1], data.bbox_utm[1][1]], - z: [data.u_min, data.u_max], - }; + const cornerPoints: Vec3[] = data.bbox_utm.map((point) => ({ x: point[0], y: point[1], z: data.u_min })); + cornerPoints.push(...data.bbox_utm.map((point) => ({ x: point[0], y: point[1], z: data.u_max }))); + + return fromCornerPoints(cornerPoints); } - predictBoundingBox(): BoundingBox | null { + predictBoundingBox(): OBBox | null { const settings = this.getSettingsContext().getDelegate().getSettings(); const seismicCrosslineNumber = settings[SettingType.SEISMIC_CROSSLINE].getDelegate().getValue(); const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); const isoTimeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); const seismicCubeMeta = this._layerDelegate.getSettingsContext().getDelegate().getStoredData("seismicCubeMeta"); - if (!seismicCubeMeta || !seismicCrosslineNumber) { + if (!seismicCubeMeta || seismicCrosslineNumber === null) { return null; } @@ -103,7 +104,18 @@ export class RealizationSeismicCrosslineLayer const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); - return { x: [rotatedMinXY.x, rotatedMaxXY.x], y: [rotatedMinXY.y, rotatedMaxXY.y], z: [zmin, zmax] }; + const cornerPoints: Vec3[] = [ + { x: rotatedMinXY.x, y: rotatedMinXY.y, z: zmin }, + { x: rotatedMaxXY.x, y: rotatedMinXY.y, z: zmin }, + { x: rotatedMaxXY.x, y: rotatedMaxXY.y, z: zmin }, + { x: rotatedMinXY.x, y: rotatedMaxXY.y, z: zmin }, + { x: rotatedMinXY.x, y: rotatedMinXY.y, z: zmax }, + { x: rotatedMaxXY.x, y: rotatedMinXY.y, z: zmax }, + { x: rotatedMaxXY.x, y: rotatedMaxXY.y, z: zmax }, + { x: rotatedMinXY.x, y: rotatedMaxXY.y, z: zmax }, + ]; + + return fromCornerPoints(cornerPoints); } makeValueRange(): [number, number] | null { diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts index 70d3b1faf..d85eb0c74 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts @@ -1,8 +1,11 @@ import { getDepthSliceOptions } from "@api"; +import { OBBox, fromCornerPoints } from "@lib/utils/orientedBoundingBox"; +import { rotatePoint2Around } from "@lib/utils/vec2"; +import { Vec3 } from "@lib/utils/vec3"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { QueryClient } from "@tanstack/react-query"; @@ -10,14 +13,19 @@ import { QueryClient } from "@tanstack/react-query"; import { isEqual } from "lodash"; import { RealizationSeismicDepthSliceSettingsContext } from "./RealizationSeismicDepthSliceSettingsContext"; -import { RealizationSeismicDepthSliceSettings } from "./types"; +import { RealizationSeismicDepthSliceSettings, RealizationSeismicDepthSliceStoredData } from "./types"; import { SeismicSliceData_trans, transformSeismicSlice } from "../../../settings/queries/queryDataTransforms"; export class RealizationSeismicDepthSliceLayer - implements Layer + implements + Layer { - private _layerDelegate: LayerDelegate; + private _layerDelegate: LayerDelegate< + RealizationSeismicDepthSliceSettings, + SeismicSliceData_trans, + RealizationSeismicDepthSliceStoredData + >; private _itemDelegate: ItemDelegate; constructor(layerManager: LayerManager) { @@ -38,10 +46,15 @@ export class RealizationSeismicDepthSliceLayer return this._itemDelegate; } - getLayerDelegate(): LayerDelegate { + getLayerDelegate(): LayerDelegate< + RealizationSeismicDepthSliceSettings, + SeismicSliceData_trans, + RealizationSeismicDepthSliceStoredData + > { return this._layerDelegate; } - makeBoundingBox(): BoundingBox | null { + + makeBoundingBox(): OBBox | null { const data = this._layerDelegate.getData(); if (!data) { return null; @@ -54,12 +67,55 @@ export class RealizationSeismicDepthSliceLayer return null; } - return { - x: [data.bbox_utm[0][0], data.bbox_utm[1][0]], - y: [data.bbox_utm[0][1], data.bbox_utm[1][1]], - z: [seismicDepth, seismicDepth], - }; + // The endpoint returns the bounding box as four points + + const cornerPoints: Vec3[] = data.bbox_utm.map((point) => { + return { x: point[0], y: point[1], z: seismicDepth }; + }); + + const obbox = fromCornerPoints(cornerPoints); + + return obbox; } + + predictBoundingBox(): OBBox | null { + const settings = this.getSettingsContext().getDelegate().getSettings(); + const seismicDepthSliceNumber = settings[SettingType.SEISMIC_DEPTH_SLICE].getDelegate().getValue(); + const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); + const isoTimeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); + const seismicCubeMeta = this._layerDelegate.getSettingsContext().getDelegate().getStoredData("seismicCubeMeta"); + + if (!seismicCubeMeta || seismicDepthSliceNumber === null) { + return null; + } + + const meta = seismicCubeMeta.find( + (m) => m.seismicAttribute === seismicAttribute && m.isoDateOrInterval === isoTimeOrInterval + ); + + if (!meta) { + return null; + } + + const xmin = meta.spec.xOrigin; + const xmax = meta.spec.xOrigin + meta.spec.xInc * (meta.spec.numCols - 1); + + const ymin = meta.spec.yOrigin; + const ymax = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * (meta.spec.numRows - 1); + + const zmin = -seismicDepthSliceNumber; + const zmax = zmin; + + const maxXY = { x: xmax, y: ymax }; + const minXY = { x: xmin, y: ymin }; + const origin = { x: meta.spec.xOrigin, y: meta.spec.yOrigin }; + + const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); + const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); + + return { x: [rotatedMinXY.x, rotatedMaxXY.x], y: [rotatedMinXY.y, rotatedMaxXY.y], z: [zmin, zmax] }; + } + doSettingsChangesRequireDataRefetch( prevSettings: RealizationSeismicDepthSliceSettings, newSettings: RealizationSeismicDepthSliceSettings diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts index 6b600495e..ecdfc311d 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts @@ -9,15 +9,21 @@ import { SeismicDepthSliceSetting } from "@modules/_shared/LayerFramework/settin import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { RealizationSeismicDepthSliceSettings } from "./types"; +import { RealizationSeismicDepthSliceSettings, RealizationSeismicDepthSliceStoredData } from "./types"; export class RealizationSeismicDepthSliceSettingsContext - implements SettingsContext + implements SettingsContext { - private _contextDelegate: SettingsContextDelegate; + private _contextDelegate: SettingsContextDelegate< + RealizationSeismicDepthSliceSettings, + RealizationSeismicDepthSliceStoredData + >; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate(this, layerManager, { + this._contextDelegate = new SettingsContextDelegate< + RealizationSeismicDepthSliceSettings, + RealizationSeismicDepthSliceStoredData + >(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), [SettingType.ATTRIBUTE]: new AttributeSetting(), @@ -35,7 +41,10 @@ export class RealizationSeismicDepthSliceSettingsContext ); } - getDelegate(): SettingsContextDelegate { + getDelegate(): SettingsContextDelegate< + RealizationSeismicDepthSliceSettings, + RealizationSeismicDepthSliceStoredData + > { return this._contextDelegate; } @@ -46,8 +55,9 @@ export class RealizationSeismicDepthSliceSettingsContext defineDependencies({ helperDependency, availableSettingsUpdater, + storedDataUpdater, queryClient, - }: DefineDependenciesArgs) { + }: DefineDependenciesArgs) { availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { const fieldIdentifier = getGlobalSetting("fieldId"); const ensembles = getGlobalSetting("ensembles"); @@ -71,7 +81,8 @@ export class RealizationSeismicDepthSliceSettingsContext return [...realizations]; }); - const RealizationSeismicDepthSliceDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + + const realizationSeismicDepthSliceDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); const realization = getLocalSetting(SettingType.REALIZATION); @@ -90,8 +101,18 @@ export class RealizationSeismicDepthSliceSettingsContext }); }); + storedDataUpdater("seismicCubeMeta", ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSeismicDepthSliceDataDep); + + if (!data) { + return null; + } + + return data; + }); + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { - const data = getHelperDependency(RealizationSeismicDepthSliceDataDep); + const data = getHelperDependency(realizationSeismicDepthSliceDataDep); if (!data) { return []; @@ -107,7 +128,7 @@ export class RealizationSeismicDepthSliceSettingsContext availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); - const data = getHelperDependency(RealizationSeismicDepthSliceDataDep); + const data = getHelperDependency(realizationSeismicDepthSliceDataDep); if (!seismicAttribute || !data) { return []; @@ -125,10 +146,11 @@ export class RealizationSeismicDepthSliceSettingsContext return availableTimeOrIntervals; }); + availableSettingsUpdater(SettingType.SEISMIC_DEPTH_SLICE, ({ getLocalSetting, getHelperDependency }) => { const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); const timeOrInterval = getLocalSetting(SettingType.TIME_OR_INTERVAL); - const data = getHelperDependency(RealizationSeismicDepthSliceDataDep); + const data = getHelperDependency(realizationSeismicDepthSliceDataDep); if (!seismicAttribute || !timeOrInterval || !data) { return []; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts index e2437beaf..63cd2b539 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts @@ -1,3 +1,4 @@ +import { SeismicCubeMeta_api } from "@api"; import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; @@ -8,3 +9,7 @@ export type RealizationSeismicDepthSliceSettings = { [SettingType.TIME_OR_INTERVAL]: string | null; [SettingType.SEISMIC_DEPTH_SLICE]: number | null; }; + +export type RealizationSeismicDepthSliceStoredData = { + seismicCubeMeta: SeismicCubeMeta_api[]; +}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index 83ff8db9c..98211124c 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -4,58 +4,6 @@ import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visua import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; import { SeismicFenceMeshLayer } from "@modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer"; -/* - * Generate a mesh for a seismic fence plot - * @param numSamplesU The number of samples in the U direction, which is the first axis and often in depth direction - * @param numSamplesV The number of samples in the V direction, which is the second axis and often in inline/crossline direction - * @param transformUVToXYZ A function that takes the U and V coordinates (∈ [0,1]) and returns the X, Y, and Z coordinates - * - * @returns The vertices and indices of the mesh - */ -function generatePointFenceMesh( - numSamplesU: number, - numSamplesV: number, - transformUVToXYZ: (u: number, v: number) => [number, number, number] -): { - vertices: Float32Array; - indices: Uint32Array; -} { - const vertices = new Float32Array(numSamplesU * numSamplesV * 3); - - // Number of triangles to be drawn - const numTriangles = (numSamplesU - 1) * (numSamplesV - 1) * 2; - - // Triangle strip indices - const indices = new Uint32Array(numTriangles * 3); - - const stepU = 1.0 / (numSamplesU - 1); - const stepV = 1.0 / (numSamplesV - 1); - - let verticesIndex = 0; - let indicesIndex = 0; - - for (let v = 0; v < numSamplesV; v++) { - for (let u = 0; u < numSamplesU; u++) { - const [x, y, z] = transformUVToXYZ(u * stepU, v * stepV); - vertices[verticesIndex++] = x; - vertices[verticesIndex++] = y; - vertices[verticesIndex++] = z; - - if (u > 0 && v > 0) { - indices[indicesIndex++] = (v - 1) * numSamplesU + u - 1; - indices[indicesIndex++] = (v - 1) * numSamplesU + u; - indices[indicesIndex++] = v * numSamplesU + u - 1; - - indices[indicesIndex++] = v * numSamplesU + u - 1; - indices[indicesIndex++] = (v - 1) * numSamplesU + u; - indices[indicesIndex++] = v * numSamplesU + u; - } - } - } - - return { vertices, indices }; -} - export enum Plane { CROSSLINE = "CROSSLINE", INLINE = "INLINE", @@ -70,7 +18,7 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { colorScale, settings, isLoading, - predictedNextBoundingBox, + predictedNextOrientedBoundingBox: predictedNextBoundingBox, }: VisualizationFunctionArgs): Layer { let bbox: number[][] = [ [data.bbox_utm[0][0], data.bbox_utm[0][1], data.u_min], @@ -82,80 +30,30 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { if (plane === Plane.DEPTH) { bbox = [ [data.bbox_utm[0][0], data.bbox_utm[0][1], settings.seismicDepthSlice], - [data.bbox_utm[0][0], data.bbox_utm[1][1], settings.seismicDepthSlice], - [data.bbox_utm[1][0], data.bbox_utm[0][1], settings.seismicDepthSlice], + [data.bbox_utm[3][0], data.bbox_utm[3][1], settings.seismicDepthSlice], [data.bbox_utm[1][0], data.bbox_utm[1][1], settings.seismicDepthSlice], + [data.bbox_utm[2][0], data.bbox_utm[2][1], settings.seismicDepthSlice], ]; } if (isLoading && predictedNextBoundingBox) { - bbox = [ - [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], predictedNextBoundingBox.z[0]], - [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], predictedNextBoundingBox.z[0]], - [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], predictedNextBoundingBox.z[1]], - [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], predictedNextBoundingBox.z[1]], - ]; - } - - /* - let adjustedData = data; - if (isLoading && predictedNextBoundingBox) { - bbox = [ - [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], predictedNextBoundingBox.z[0]], - [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], predictedNextBoundingBox.z[1]], - ]; - adjustedData = { - ...data, - u_num_samples: 2, - v_num_samples: 2, - }; - } - const properties = data.dataFloat32Arr; - - let startPosition: [number, number, number] = [0, 0, 0]; - let boundingBox: number[][] = []; - - let transformUVToXYZ: (u: number, v: number) => [number, number, number] = () => { - throw new Error("transformUVToXYZ not implemented"); - }; - - if (plane === Plane.CROSSLINE || plane === Plane.INLINE) { - startPosition = [bbox[0][0], bbox[0][1], -data.u_min]; - boundingBox = [ - startPosition, - [bbox[1][0], bbox[1][1], -data.u_min], - [bbox[1][0], bbox[1][1], -data.u_max], - [bbox[0][0], bbox[0][1], -data.u_max], - ]; - transformUVToXYZ = (u: number, v: number): [number, number, number] => { - const x = v * (bbox[1][0] - bbox[0][0]); - const y = v * (bbox[1][1] - bbox[0][1]); - const z = -(u * (data.u_max - data.u_min)); - return [x, y, z]; - }; - } else if (plane === Plane.DEPTH) { - startPosition = [bbox[0][0], bbox[0][1], -settings.seismicDepthSlice]; - boundingBox = [ - startPosition, - [bbox[1][0], bbox[1][1], -settings.seismicDepthSlice], - [bbox[2][0], bbox[2][1], -settings.seismicDepthSlice], - [bbox[3][0], bbox[3][1], -settings.seismicDepthSlice], - ]; - transformUVToXYZ = (u: number, v: number): [number, number, number] => { - const x = u * (bbox[1][0] - bbox[0][0]) + v * (bbox[3][0] - bbox[0][0]); // Diagonal across bounding box - const y = u * (bbox[1][1] - bbox[0][1]) + v * (bbox[3][1] - bbox[0][1]); // Diagonal across bounding box - const z = 0; - return [x, y, z]; - }; + if (plane === Plane.DEPTH) { + bbox = [ + [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], settings.seismicDepthSlice], + [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], settings.seismicDepthSlice], + [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], settings.seismicDepthSlice], + [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], settings.seismicDepthSlice], + ]; + } else { + bbox = [ + [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], predictedNextBoundingBox.z[0]], + [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], predictedNextBoundingBox.z[0]], + [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], predictedNextBoundingBox.z[1]], + [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], predictedNextBoundingBox.z[1]], + ]; + } } - const { vertices, indices } = generatePointFenceMesh( - adjustedData.u_num_samples, - adjustedData.v_num_samples, - transformUVToXYZ - ); - */ - return new SeismicFenceMeshLayer({ id, name, diff --git a/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx index 2cf9f8de1..0ba9f8fcf 100644 --- a/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx @@ -16,7 +16,7 @@ import { Toolbar } from "./Toolbar"; import { DeckGlInstanceManager, DeckGlInstanceManagerTopic } from "../utils/DeckGlInstanceManager"; import { Polyline, PolylinesPlugin, PolylinesPluginTopic } from "../utils/PolylinesPlugin"; -export type InteractionWrapperProps = {} & Omit< +export type InteractionWrapperProps = Omit< ReadooutWrapperProps, "deckGlManager" | "triggerHome" | "verticalScale" | "deckGlRef" >; @@ -127,7 +127,7 @@ export function InteractionWrapper(props: InteractionWrapperProps): React.ReactN [activePolylineId] ); - let adjustedLayers: DeckGlLayer[] = []; + let adjustedLayers: DeckGlLayer[] = [...props.layers]; if (!gridVisible) { adjustedLayers = adjustedLayers.filter((layer) => !(layer instanceof AxesLayer)); } diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index cb0f6eb8f..52d8f58f4 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -31,8 +31,8 @@ import { VisualizationFactory, VisualizationTarget, } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import { makeWellborePicksLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer"; -import { makeWellsLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/wellsLayer"; +import { makeWellborePicksLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer"; +import { makeWellsLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer"; import { ColorLegendsContainer } from "@modules/_shared/components/ColorLegendsContainer"; import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts index b4f31ca37..e021ef4a4 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts @@ -1,6 +1,7 @@ import { StatusMessage } from "@framework/ModuleInstanceStatusController"; import { ApiErrorHelper } from "@framework/utils/ApiErrorHelper"; import { isDevMode } from "@lib/utils/devMode"; +import { OBBox, clone } from "@lib/utils/orientedBoundingBox"; import { QueryClient, isCancelledError } from "@tanstack/react-query"; import { SettingsContextDelegateTopic } from "./SettingsContextDelegate"; @@ -9,15 +10,7 @@ import { UnsubscribeHandlerDelegate } from "./UnsubscribeHandlerDelegate"; import { PublishSubscribe, PublishSubscribeDelegate } from "../../utils/PublishSubscribeDelegate"; import { LayerManager, LayerManagerTopic } from "../framework/LayerManager/LayerManager"; import { SharedSetting } from "../framework/SharedSetting/SharedSetting"; -import { - BoundingBox, - Layer, - SerializedLayer, - SerializedType, - Settings, - SettingsContext, - StoredData, -} from "../interfaces"; +import { Layer, SerializedLayer, SerializedType, Settings, SettingsContext, StoredData } from "../interfaces"; export enum LayerDelegateTopic { STATUS = "STATUS", @@ -62,9 +55,9 @@ export class LayerDelegate; doSettingsChangesRequireDataRefetch(prevSettings: TSettings, newSettings: TSettings): boolean; fetchData(queryClient: QueryClient): Promise; - makeBoundingBox?(): BoundingBox | null; - predictBoundingBox?(): BoundingBox | null; + makeBoundingBox?(): OBBox | null; + predictBoundingBox?(): OBBox | null; makeValueRange?(): [number, number] | null; } diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesLayer.ts index 1c1641448..e27bf3189 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesLayer.ts @@ -1,8 +1,10 @@ import { WellboreTrajectory_api, getWellTrajectoriesOptions } from "@api"; +import { BBox } from "@lib/utils/boundingBox"; +import { OBBox, fromAxisAlignedBoundingBox } from "@lib/utils/orientedBoundingBox"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { QueryClient } from "@tanstack/react-query"; @@ -45,34 +47,33 @@ export class DrilledWellTrajectoriesLayer implements Layer { diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksLayer.ts index 3014e1d85..6f9bb8c7c 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksLayer.ts @@ -1,8 +1,10 @@ import { WellborePick_api, getWellborePicksForPickIdentifierOptions } from "@api"; +import { BBox } from "@lib/utils/boundingBox"; +import { OBBox, fromAxisAlignedBoundingBox } from "@lib/utils/orientedBoundingBox"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { QueryClient } from "@tanstack/react-query"; @@ -45,30 +47,27 @@ export class DrilledWellborePicksLayer implements Layer { diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts index 19dd7c6d4..e01ebdced 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts @@ -3,6 +3,7 @@ import { Layer as EsvLayer } from "@equinor/esv-intersection"; import { StatusMessage } from "@framework/ModuleInstanceStatusController"; import { defaultColorPalettes, defaultContinuousSequentialColorPalettes } from "@framework/utils/colorPalettes"; import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; +import * as obbox from "@lib/utils/orientedBoundingBox"; import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; @@ -12,7 +13,7 @@ import { ColorScale } from "../framework/ColorScale/ColorScale"; import { DeltaSurface } from "../framework/DeltaSurface/DeltaSurface"; import { LayerManager } from "../framework/LayerManager/LayerManager"; import { View } from "../framework/View/View"; -import { BoundingBox, Layer, Settings, instanceofGroup, instanceofLayer } from "../interfaces"; +import { Layer, Settings, instanceofGroup, instanceofLayer } from "../interfaces"; export enum VisualizationTarget { DECK_GL = "deck_gl", @@ -27,7 +28,8 @@ export type VisualizationFunctionArgs = { colorScale: ColorScaleWithName; settings: TSettings; isLoading: boolean; - predictedNextBoundingBox: BoundingBox | null; + orientedBoundingBox: obbox.OBBox | null; + predictedNextOrientedBoundingBox: obbox.OBBox | null; }; export type TargetReturnTypes = { @@ -56,7 +58,7 @@ export type FactoryProduct = { views: VisualizationView[]; layers: LayerWithPosition[]; errorMessages: (StatusMessage | string)[]; - boundingBox: BoundingBox | null; + boundingBox: obbox.OBBox | null; colorScales: ColorScaleWithId[]; numLoadingLayers: number; }; @@ -84,11 +86,11 @@ export class VisualizationFactory { const collectedColorScales: ColorScaleWithId[] = []; const collectedErrorMessages: (StatusMessage | string)[] = []; let collectedNumLoadingLayers = 0; - let globalBoundingBox: BoundingBox | null = null; + let globalBoundingBox: obbox.OBBox | null = null; const children = groupDelegate.getChildren(); - const maybeApplyBoundingBox = (boundingBox: BoundingBox | null) => { + const maybeApplyBoundingBox = (boundingBox: obbox.OBBox | null) => { if (boundingBox) { globalBoundingBox = globalBoundingBox === null ? boundingBox : this.makeNewBoundingBox(boundingBox, globalBoundingBox); @@ -154,7 +156,7 @@ export class VisualizationFactory { collectedColorScales.push(colorScale); } - const boundingBox = child.getLayerDelegate().getBoundingBox(); + const boundingBox = child.getLayerDelegate().getOrientedBoundingBox(); maybeApplyBoundingBox(boundingBox); collectedLayers.push({ layer, position: numCollectedLayers + collectedLayers.length }); } @@ -193,16 +195,13 @@ export class VisualizationFactory { colorScale, settings: layer.getLayerDelegate().getSettingsContext().getDelegate().getValues(), isLoading: layer.getLayerDelegate().getStatus() === LayerStatus.LOADING, - predictedNextBoundingBox: layer.getLayerDelegate().getPredictedBoundingBox(), + orientedBoundingBox: layer.getLayerDelegate().getOrientedBoundingBox(), + predictedNextOrientedBoundingBox: layer.getLayerDelegate().getPredictedOrientedBoundingBox(), }); } - private makeNewBoundingBox(newBoundingBox: BoundingBox, oldBoundingBox: BoundingBox): BoundingBox { - return { - x: [Math.min(newBoundingBox.x[0], oldBoundingBox.x[0]), Math.max(newBoundingBox.x[1], oldBoundingBox.x[1])], - y: [Math.min(newBoundingBox.y[0], oldBoundingBox.y[0]), Math.max(newBoundingBox.y[1], oldBoundingBox.y[1])], - z: [Math.min(newBoundingBox.z[0], oldBoundingBox.z[0]), Math.max(newBoundingBox.z[1], oldBoundingBox.z[1])], - }; + private makeNewBoundingBox(newBoundingBox: obbox.OBBox, oldBoundingBox: obbox.OBBox): obbox.OBBox { + return obbox.combine(newBoundingBox, oldBoundingBox); } private findColorScale(layer: Layer): { id: string; colorScale: ColorScaleWithName } | null { diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer.ts similarity index 100% rename from frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellborePicksLayer.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer.ts diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellsLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts similarity index 88% rename from frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellsLayer.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts index 1b33a0494..edc912ea0 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/wellsLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts @@ -1,4 +1,6 @@ import { WellboreTrajectory_api } from "@api"; +import * as bbox from "@lib/utils/boundingBox"; +import * as obbox from "@lib/utils/orientedBoundingBox"; import { Feature, FeatureCollection, GeoJsonProperties, GeometryCollection, LineString, Point } from "geojson"; @@ -55,6 +57,7 @@ export function makeWellsLayer({ id, data, name, + orientedBoundingBox, }: VisualizationFunctionArgs): AdvancedWellsLayer { // Filter out some wellbores that are known to be not working - this is a temporary solution const filteredData = data.filter((wellbore) => wellbore.uniqueWellboreIdentifier !== "NO 34/4-K-3 AH"); @@ -65,12 +68,16 @@ export function makeWellsLayer({ features: filteredData.map((wellTrajectory) => wellTrajectoryToGeojson(wellTrajectory)), }; + const bbox3d = orientedBoundingBox + ? bbox.toNumArray(obbox.toAxisAlignedBoundingBox(orientedBoundingBox)) + : undefined; + return new AdvancedWellsLayer({ id, data: featureCollection, name, refine: false, - wellNameVisible: true, + wellNameVisible: false, wellHeadStyle: { size: 1 }, pickable: true, ZIncreasingDownwards: false, @@ -78,5 +85,6 @@ export function makeWellsLayer({ lineStyle: { width: 2, }, + boundingBox: bbox3d, }); } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/AdvancedWellsLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/AdvancedWellsLayer.ts index 117c42aee..8838756ab 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/AdvancedWellsLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/AdvancedWellsLayer.ts @@ -1,63 +1,30 @@ -import { FilterContext, Layer, LayersList } from "@deck.gl/core"; -import { GeoJsonLayer } from "@deck.gl/layers"; -import { WellsLayer } from "@webviz/subsurface-viewer/dist/layers"; +import { CompositeLayer, CompositeLayerProps, Layer, LayersList, UpdateParameters } from "@deck.gl/core"; +import { BoundingBox3D } from "@webviz/subsurface-viewer"; +import { WellsLayer, WellsLayerProps } from "@webviz/subsurface-viewer/dist/layers"; -export class AdvancedWellsLayer extends WellsLayer { - static layerName: string = "WellsLayer"; +export type AdvancedWellsLayerProps = { + boundingBox: BoundingBox3D; +} & WellsLayerProps; +export class AdvancedWellsLayer extends CompositeLayer { + static layerName: string = "AdvancedWellsLayer"; - filterSubLayer(context: FilterContext): boolean { - if (context.layer.id.includes("names")) { - return context.viewport.zoom > -2; - } + updateState(params: UpdateParameters>>): void { + super.updateState(params); - return true; - } - - renderLayers(): LayersList { - const layers = super.renderLayers(); - - if (!Array.isArray(layers)) { - return layers; - } - - const colorsLayer = layers.find((layer) => { - if (!(layer instanceof Layer)) { - return false; - } + const { boundingBox } = this.props; - return layer.id.includes("colors"); - }); - - if (!(colorsLayer instanceof GeoJsonLayer)) { - return layers; - } - - const newColorsLayer = new GeoJsonLayer({ - data: colorsLayer.props.data, - pickable: true, - stroked: false, - positionFormat: colorsLayer.props.positionFormat, - pointRadiusUnits: "meters", - lineWidthUnits: "meters", - pointRadiusScale: this.props.pointRadiusScale, - lineWidthScale: this.props.lineWidthScale, - getLineWidth: colorsLayer.props.getLineWidth, - getPointRadius: colorsLayer.props.getPointRadius, - lineBillboard: true, - pointBillboard: true, - parameters: colorsLayer.props.parameters, - visible: colorsLayer.props.visible, - id: "colors", - lineWidthMinPixels: 1, - lineWidthMaxPixels: 5, - extensions: colorsLayer.props.extensions, - getDashArray: colorsLayer.props.getDashArray, - getLineColor: colorsLayer.props.getLineColor, - getFillColor: colorsLayer.props.getFillColor, - autoHighlight: true, - onHover: () => {}, + this.props.reportBoundingBox?.({ + layerBoundingBox: boundingBox, }); + } - return [newColorsLayer, ...layers.filter((layer) => layer !== colorsLayer)]; + renderLayers(): LayersList { + return [ + new WellsLayer({ + ...this.props, + id: "wellslayer", + data: this.props.data, + }), + ]; } } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts index 92b0b036e..9238d813a 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts @@ -282,7 +282,6 @@ export class SeismicFenceMeshLayer extends CompositeLayer Date: Thu, 20 Feb 2025 11:04:37 +0100 Subject: [PATCH 56/97] wip --- frontend/src/lib/utils/boundingBox.ts | 49 ++++++++++++-- frontend/src/lib/utils/orientedBoundingBox.ts | 65 ++++++++++++------- .../ObservedSurfaceLayer.ts | 20 ++++-- .../RealizationGridLayer.ts | 26 ++++---- .../RealizationPolygonsLayer.ts | 26 ++++---- .../RealizationSurfaceLayer.ts | 14 ++-- .../StatisticalSurfaceLayer.ts | 16 +++-- .../view/components/LayersWrapper.tsx | 23 ++----- .../2DViewer/view/utils/makeViewsAndLayers.ts | 19 +++--- .../IntersectionRealizationGridLayer.ts | 5 +- .../LayerFramework/delegates/LayerDelegate.ts | 24 +++---- .../_shared/LayerFramework/interfaces.ts | 6 +- .../visualization/VisualizationFactory.ts | 6 +- 13 files changed, 175 insertions(+), 124 deletions(-) diff --git a/frontend/src/lib/utils/boundingBox.ts b/frontend/src/lib/utils/boundingBox.ts index bbc2fdaf1..5e15ddcc6 100644 --- a/frontend/src/lib/utils/boundingBox.ts +++ b/frontend/src/lib/utils/boundingBox.ts @@ -7,7 +7,7 @@ export type BBox = { max: vec3.Vec3; }; -/* +/** Creates a new bounding box. */ export function create(min: vec3.Vec3, max: vec3.Vec3): BBox { @@ -21,7 +21,7 @@ export function fromBoundingBox3DApi(boundingBox: BoundingBox3D_api): BBox { ); } -/* +/** Returns true if the bounding box contains the given point. */ export function containsPoint(box: BBox, point: vec3.Vec3): boolean { @@ -35,7 +35,7 @@ export function containsPoint(box: BBox, point: vec3.Vec3): boolean { ); } -/* +/** Returns true if the two bounding boxes intersect. */ export function intersects(box1: BBox, box2: BBox): boolean { @@ -49,7 +49,21 @@ export function intersects(box1: BBox, box2: BBox): boolean { ); } -/* +/** + * Returns true if outerBox contains innerBox. + */ +export function containsBox(outerBox: BBox, innerBox: BBox): boolean { + return ( + outerBox.min.x <= innerBox.min.x && + outerBox.min.y <= innerBox.min.y && + outerBox.min.z <= innerBox.min.z && + outerBox.max.x >= innerBox.max.x && + outerBox.max.y >= innerBox.max.y && + outerBox.max.z >= innerBox.max.z + ); +} + +/** Converts a bounding box to an array of numbers. The array contains the following numbers in the following order: [min.x, min.y, min.z, max.x, max.y, max.z] @@ -58,7 +72,7 @@ export function toNumArray(box: BBox): [number, number, number, number, number, return [box.min.x, box.min.y, box.min.z, box.max.x, box.max.y, box.max.z]; } -/* +/** Converts an array of numbers to a bounding box. The array should contain the following numbers in the following order: [min.x, min.y, min.z, max.x, max.y, max.z] @@ -66,3 +80,28 @@ export function toNumArray(box: BBox): [number, number, number, number, number, export function fromNumArray(array: [number, number, number, number, number, number]): BBox { return create(vec3.fromArray(array.slice(0, 3)), vec3.fromArray(array.slice(3, 6))); } + +/** + * Clones the given bounding box. + */ +export function clone(box: BBox): BBox { + return create(vec3.clone(box.min), vec3.clone(box.max)); +} + +/** + * Combines the two bounding boxes into a new bounding box that contains both. + */ +export function combine(box1: BBox, box2: BBox): BBox { + return create( + vec3.create( + Math.min(box1.min.x, box2.min.x), + Math.min(box1.min.y, box2.min.y), + Math.min(box1.min.z, box2.min.z) + ), + vec3.create( + Math.max(box1.max.x, box2.max.x), + Math.max(box1.max.y, box2.max.y), + Math.max(box1.max.z, box2.max.z) + ) + ); +} diff --git a/frontend/src/lib/utils/orientedBoundingBox.ts b/frontend/src/lib/utils/orientedBoundingBox.ts index ea6823c56..989d15608 100644 --- a/frontend/src/lib/utils/orientedBoundingBox.ts +++ b/frontend/src/lib/utils/orientedBoundingBox.ts @@ -7,16 +7,16 @@ export type OBBox = { halfExtents: number[]; }; -/* - Creates a new oriented bounding box. - */ +/** + * Creates a new oriented bounding box. + */ export function create(center: vec3.Vec3, principalAxes: vec3.Vec3[], halfExtents: number[]): OBBox { return { centerPoint: center, principalAxes, halfExtents }; } /* - Returns true if the oriented bounding box contains the given point. - */ + * Returns true if the oriented bounding box contains the given point. + */ export function containsPoint(box: OBBox, point: vec3.Vec3): boolean { const diff = vec3.subtract(point, box.centerPoint); return ( @@ -26,9 +26,9 @@ export function containsPoint(box: OBBox, point: vec3.Vec3): boolean { ); } -/* - Converts an oriented bounding box to an axis-aligned bounding box. - */ +/** + * Converts an oriented bounding box to an axis-aligned bounding box. + */ export function toAxisAlignedBoundingBox(box: OBBox): bbox.BBox { const absAxisX = vec3.abs(box.principalAxes[0]); const absAxisY = vec3.abs(box.principalAxes[1]); @@ -49,19 +49,32 @@ export function fromAxisAlignedBoundingBox(box: bbox.BBox): OBBox { return create(centerPoint, principalAxes, [halfExtents.x, halfExtents.y, halfExtents.z]); } -/* - Returns the corner points of the oriented bounding box. +/** + * Returns true if outerBox contains innerBox. + */ +export function containsBox(outerBox: OBBox, innerBox: OBBox): boolean { + const points = calcCornerPoints(innerBox); + for (const point of points) { + if (!containsPoint(outerBox, point)) { + return false; + } + } + return true; +} - The points are returned in the following order for z-axis up coordinate system: - 0: bottom front left - 1: bottom front right - 2: bottom back left - 3: bottom back right - 4: top front left - 5: top front right - 6: top back left - 7: top back right - */ +/** + * Returns the corner points of the oriented bounding box. + * + * The points are returned in the following order for z-axis up coordinate system: + * 0: bottom front left + * 1: bottom front right + * 2: bottom back left + * 3: bottom back right + * 4: top front left + * 5: top front right + * 6: top back left + * 7: top back right + */ export function calcCornerPoints(box: OBBox): vec3.Vec3[] { const halfExtents = box.halfExtents; @@ -88,6 +101,9 @@ export function calcCornerPoints(box: OBBox): vec3.Vec3[] { return points; } +/** + * Creates an oriented bounding box from the given corner points. + */ export function fromCornerPoints(points: vec3.Vec3[]): OBBox { const min = vec3.create(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); const max = vec3.create(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); @@ -109,9 +125,9 @@ export function fromCornerPoints(points: vec3.Vec3[]): OBBox { return create(center, principalAxes, [halfExtents.x, halfExtents.y, halfExtents.z]); } -/* - Combines two oriented bounding boxes into a new oriented bounding box that contains both input boxes. - */ +/** + * Combines two oriented bounding boxes into a new oriented bounding box that contains both input boxes. + */ export function combine(box1: OBBox, box2: OBBox): OBBox { const points1 = calcCornerPoints(box1); const points2 = calcCornerPoints(box2); @@ -136,6 +152,9 @@ export function combine(box1: OBBox, box2: OBBox): OBBox { return create(center, principalAxes, [halfExtents.x, halfExtents.y, halfExtents.z]); } +/** + * Clones the given oriented bounding box. + */ export function clone(box: OBBox): OBBox { return create( vec3.clone(box.centerPoint), diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts index 8c839b2fe..81277d688 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer/ObservedSurfaceLayer.ts @@ -1,8 +1,9 @@ import { SurfaceDataPng_api, getSurfaceDataOptions } from "@api"; +import * as bbox from "@lib/utils/boundingBox"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { FullSurfaceAddress, SurfaceAddressBuilder } from "@modules/_shared/Surface"; @@ -50,17 +51,22 @@ export class ObservedSurfaceLayer return !isEqual(prevSettings, newSettings); } - makeBoundingBox(): BoundingBox | null { + makeBoundingBox(): bbox.BBox | null { const data = this._layerDelegate.getData(); if (!data) { return null; } - return { - x: [data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.max_x], - y: [data.transformed_bbox_utm.min_y, data.transformed_bbox_utm.max_y], - z: [0, 0], - }; + const boundingBox = bbox.fromNumArray([ + data.transformed_bbox_utm.min_x, + data.transformed_bbox_utm.min_y, + 0, + data.transformed_bbox_utm.max_x, + data.transformed_bbox_utm.max_y, + 0, + ]); + + return boundingBox; } makeValueRange(): [number, number] | null { diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts index 8b0da89ba..4a8d2b676 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -1,4 +1,5 @@ import { getGridParameterOptions, getGridSurfaceOptions } from "@api"; +import * as bbox from "@lib/utils/boundingBox"; import { GridMappedProperty_trans, GridSurface_trans, @@ -8,7 +9,7 @@ import { import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { QueryClient } from "@tanstack/react-query"; @@ -72,23 +73,22 @@ export class RealizationGridLayer return !isEqual(prevSettings, newSettings); } - makeBoundingBox(): BoundingBox | null { + makeBoundingBox(): bbox.BBox | null { const data = this._layerDelegate.getData(); if (!data) { return null; } - return { - x: [ - data.gridSurfaceData.origin_utm_x + data.gridSurfaceData.xmin, - data.gridSurfaceData.origin_utm_x + data.gridSurfaceData.xmax, - ], - y: [ - data.gridSurfaceData.origin_utm_y + data.gridSurfaceData.ymin, - data.gridSurfaceData.origin_utm_y + data.gridSurfaceData.ymax, - ], - z: [data.gridSurfaceData.zmin, data.gridSurfaceData.zmax], - }; + const boundingBox = bbox.fromNumArray([ + data.gridSurfaceData.origin_utm_x + data.gridSurfaceData.xmin, + data.gridSurfaceData.origin_utm_y + data.gridSurfaceData.ymin, + data.gridSurfaceData.zmin, + data.gridSurfaceData.origin_utm_x + data.gridSurfaceData.xmax, + data.gridSurfaceData.origin_utm_y + data.gridSurfaceData.ymax, + data.gridSurfaceData.zmax, + ]); + + return boundingBox; } makeValueRange(): [number, number] | null { diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts index ed3d45891..b49f0bfbf 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts +++ b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer/RealizationPolygonsLayer.ts @@ -1,8 +1,9 @@ import { PolygonData_api, getPolygonsDataOptions } from "@api"; +import { BBox, create } from "@lib/utils/boundingBox"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { QueryClient } from "@tanstack/react-query"; @@ -45,30 +46,29 @@ export class RealizationPolygonsLayer implements Layer(null); + const [prevBoundingBox, setPrevBoundingBox] = React.useState(null); const mainDivRef = React.useRef(null); const mainDivSize = useElementSize(mainDivRef); @@ -100,21 +99,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { if (viewsAndLayers.boundingBox !== null) { if (prevBoundingBox !== null) { - const oldBoundingRect: Rect2D | null = { - x: prevBoundingBox.x[0], - y: prevBoundingBox.y[0], - width: prevBoundingBox.x[1] - prevBoundingBox.x[0], - height: prevBoundingBox.y[1] - prevBoundingBox.y[0], - }; - - const newBoundingRect: Rect2D = { - x: viewsAndLayers.boundingBox.x[0], - y: viewsAndLayers.boundingBox.y[0], - width: viewsAndLayers.boundingBox.x[1] - viewsAndLayers.boundingBox.x[0], - height: viewsAndLayers.boundingBox.y[1] - viewsAndLayers.boundingBox.y[0], - }; - - if (!outerRectContainsInnerRect(oldBoundingRect, newBoundingRect)) { + if (!bbox.containsBox(prevBoundingBox, viewsAndLayers.boundingBox)) { setPrevBoundingBox(viewsAndLayers.boundingBox); } } else { @@ -131,7 +116,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { let bounds: BoundingBox2D | undefined = undefined; if (prevBoundingBox) { - bounds = [prevBoundingBox.x[0], prevBoundingBox.y[0], prevBoundingBox.x[1], prevBoundingBox.y[1]]; + bounds = [prevBoundingBox.min.x, prevBoundingBox.min.y, prevBoundingBox.max.x, prevBoundingBox.max.y]; } const layers = viewerLayers.toSorted((a, b) => b.position - a.position).map((layer) => layer.layer); diff --git a/frontend/src/modules/2DViewer/view/utils/makeViewsAndLayers.ts b/frontend/src/modules/2DViewer/view/utils/makeViewsAndLayers.ts index 285f1f911..868945763 100644 --- a/frontend/src/modules/2DViewer/view/utils/makeViewsAndLayers.ts +++ b/frontend/src/modules/2DViewer/view/utils/makeViewsAndLayers.ts @@ -2,12 +2,13 @@ import { Layer as DeckGlLayer } from "@deck.gl/core"; import { StatusMessage } from "@framework/ModuleInstanceStatusController"; import { defaultContinuousSequentialColorPalettes } from "@framework/utils/colorPalettes"; import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; +import * as bbox from "@lib/utils/boundingBox"; import { GroupDelegate } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; import { LayerColoringType, LayerStatus } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { ColorScale } from "@modules/_shared/LayerFramework/framework/ColorScale/ColorScale"; import { DeltaSurface } from "@modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurface"; import { View } from "@modules/_shared/LayerFramework/framework/View/View"; -import { BoundingBox, Layer, instanceofGroup, instanceofLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, instanceofGroup, instanceofLayer } from "@modules/_shared/LayerFramework/interfaces"; import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; @@ -30,7 +31,7 @@ export type DeckGlViewsAndLayers = { views: DeckGlView[]; layers: DeckGlLayerWithPosition[]; errorMessages: (StatusMessage | string)[]; - boundingBox: BoundingBox | null; + boundingBox: bbox.BBox | null; colorScales: ColorScaleWithId[]; numLoadingLayers: number; }; @@ -44,11 +45,11 @@ export function recursivelyMakeViewsAndLayers( const collectedColorScales: ColorScaleWithId[] = []; const collectedErrorMessages: (StatusMessage | string)[] = []; let collectedNumLoadingLayers = 0; - let globalBoundingBox: BoundingBox | null = null; + let globalBoundingBox: bbox.BBox | null = null; const children = groupDelegate.getChildren(); - const maybeApplyBoundingBox = (boundingBox: BoundingBox | null) => { + const maybeApplyBoundingBox = (boundingBox: bbox.BBox | null) => { if (boundingBox) { globalBoundingBox = globalBoundingBox === null ? boundingBox : makeNewBoundingBox(boundingBox, globalBoundingBox); @@ -112,7 +113,7 @@ export function recursivelyMakeViewsAndLayers( collectedColorScales.push(colorScale); } - const boundingBox = child.getLayerDelegate().getOrientedBoundingBox(); + const boundingBox = child.getLayerDelegate().getBoundingBox(); maybeApplyBoundingBox(boundingBox); collectedLayers.push({ layer, position: numCollectedLayers + collectedLayers.length }); } @@ -174,10 +175,6 @@ function findColorScale(layer: Layer): { id: string; colorScale: Color }; } -function makeNewBoundingBox(newBoundingBox: BoundingBox, oldBoundingBox: BoundingBox): BoundingBox { - return { - x: [Math.min(newBoundingBox.x[0], oldBoundingBox.x[0]), Math.max(newBoundingBox.x[1], oldBoundingBox.x[1])], - y: [Math.min(newBoundingBox.y[0], oldBoundingBox.y[0]), Math.max(newBoundingBox.y[1], oldBoundingBox.y[1])], - z: [Math.min(newBoundingBox.z[0], oldBoundingBox.z[0]), Math.max(newBoundingBox.z[1], oldBoundingBox.z[1])], - }; +function makeNewBoundingBox(newBoundingBox: bbox.BBox, oldBoundingBox: bbox.BBox): bbox.BBox { + return bbox.combine(newBoundingBox, oldBoundingBox); } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts index 52832fee4..fa1588561 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts @@ -1,9 +1,10 @@ import { getWellTrajectoriesOptions, postGetPolylineIntersectionOptions } from "@api"; import { IntersectionReferenceSystem } from "@equinor/esv-intersection"; +import * as bbox from "@lib/utils/boundingBox"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { @@ -53,7 +54,7 @@ export class IntersectionRealizationGridLayer return !isEqual(prevSettings, newSettings); } - makeBoundingBox(): BoundingBox | null { + makeBoundingBox(): bbox.BBox | null { const data = this._layerDelegate.getData(); if (!data) { return null; diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts index e021ef4a4..83e45c602 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts @@ -1,7 +1,7 @@ import { StatusMessage } from "@framework/ModuleInstanceStatusController"; import { ApiErrorHelper } from "@framework/utils/ApiErrorHelper"; +import * as bbox from "@lib/utils/boundingBox"; import { isDevMode } from "@lib/utils/devMode"; -import { OBBox, clone } from "@lib/utils/orientedBoundingBox"; import { QueryClient, isCancelledError } from "@tanstack/react-query"; import { SettingsContextDelegateTopic } from "./SettingsContextDelegate"; @@ -55,9 +55,9 @@ export class LayerDelegate; doSettingsChangesRequireDataRefetch(prevSettings: TSettings, newSettings: TSettings): boolean; fetchData(queryClient: QueryClient): Promise; - makeBoundingBox?(): OBBox | null; - predictBoundingBox?(): OBBox | null; + makeBoundingBox?(): BBox | null; + predictBoundingBox?(): BBox | null; makeValueRange?(): [number, number] | null; } diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts index e01ebdced..0b774a4ea 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts @@ -156,7 +156,7 @@ export class VisualizationFactory { collectedColorScales.push(colorScale); } - const boundingBox = child.getLayerDelegate().getOrientedBoundingBox(); + const boundingBox = child.getLayerDelegate().getBoundingBox(); maybeApplyBoundingBox(boundingBox); collectedLayers.push({ layer, position: numCollectedLayers + collectedLayers.length }); } @@ -195,8 +195,8 @@ export class VisualizationFactory { colorScale, settings: layer.getLayerDelegate().getSettingsContext().getDelegate().getValues(), isLoading: layer.getLayerDelegate().getStatus() === LayerStatus.LOADING, - orientedBoundingBox: layer.getLayerDelegate().getOrientedBoundingBox(), - predictedNextOrientedBoundingBox: layer.getLayerDelegate().getPredictedOrientedBoundingBox(), + orientedBoundingBox: layer.getLayerDelegate().getBoundingBox(), + predictedNextOrientedBoundingBox: layer.getLayerDelegate().getPredictedBoundingBox(), }); } From 9ec86b6241315873a60c5141d39db258155fb0c3 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 20 Feb 2025 14:52:43 +0100 Subject: [PATCH 57/97] wip --- frontend/src/lib/utils/boundingBox.ts | 2 +- frontend/src/lib/utils/geometry.ts | 17 ++++ frontend/src/lib/utils/index.ts | 1 + .../view/components/LayersWrapper.tsx | 2 +- .../IntersectionRealizationGridLayer.ts | 20 ++++- .../RealizationGridLayer.ts | 25 +++--- .../RealizationSeismicCrosslineLayer.ts | 44 ++++++---- .../RealizationSeismicDepthSliceLayer.ts | 46 +++++++--- .../RealizationSeismicInlineLayer.ts | 87 ++++++++++++++++--- ...RealizationSeismicInlineSettingsContext.ts | 20 +++-- .../RealizationSeismicInlineLayer/types.ts | 5 ++ .../RealizationSurfaceLayer.ts | 11 +-- .../makeSeismicFenceMeshLayer.ts | 3 +- .../view/components/LayersWrapper.tsx | 34 +------- .../LayerFramework/delegates/LayerDelegate.ts | 14 ++- .../_shared/LayerFramework/interfaces.ts | 20 ++++- .../DrilledWellTrajectoriesLayer.ts | 28 +++--- .../DrilledWellborePicksLayer.ts | 28 +++--- .../IntersectionRealizationGridLayer.ts | 5 +- .../visualization/VisualizationFactory.ts | 23 ++--- .../visualization/deckgl/makeWellsLayer.ts | 7 +- 21 files changed, 289 insertions(+), 153 deletions(-) create mode 100644 frontend/src/lib/utils/index.ts diff --git a/frontend/src/lib/utils/boundingBox.ts b/frontend/src/lib/utils/boundingBox.ts index 5e15ddcc6..15dc92f01 100644 --- a/frontend/src/lib/utils/boundingBox.ts +++ b/frontend/src/lib/utils/boundingBox.ts @@ -52,7 +52,7 @@ export function intersects(box1: BBox, box2: BBox): boolean { /** * Returns true if outerBox contains innerBox. */ -export function containsBox(outerBox: BBox, innerBox: BBox): boolean { +export function outerBoxcontainsInnerBox(outerBox: BBox, innerBox: BBox): boolean { return ( outerBox.min.x <= innerBox.min.x && outerBox.min.y <= innerBox.min.y && diff --git a/frontend/src/lib/utils/geometry.ts b/frontend/src/lib/utils/geometry.ts index 96f17477a..e2473b1b2 100644 --- a/frontend/src/lib/utils/geometry.ts +++ b/frontend/src/lib/utils/geometry.ts @@ -1,4 +1,5 @@ import type { Vec2 } from "./vec2"; +import { Vec3 } from "./vec3"; export type Size2D = { width: number; @@ -21,6 +22,22 @@ export type Rect3D = { depth: number; }; +export enum GeometryType { + POLYGON = "polygon", + ELEVATED_POLYGON = "elevated_polygon", +} + +export type Geometry = + | { + type: GeometryType.POLYGON; + points: Vec3[]; + } + | { + type: GeometryType.ELEVATED_POLYGON; + points: Vec3[]; + height: number; + }; + export const ORIGIN = Object.freeze({ x: 0, y: 0 }); export const MANHATTAN_LENGTH = 13.11; diff --git a/frontend/src/lib/utils/index.ts b/frontend/src/lib/utils/index.ts new file mode 100644 index 000000000..a8c6b55be --- /dev/null +++ b/frontend/src/lib/utils/index.ts @@ -0,0 +1 @@ +export * as bbox from "./boundingBox"; diff --git a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx index bec8d1757..760d898f1 100644 --- a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx @@ -99,7 +99,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { if (viewsAndLayers.boundingBox !== null) { if (prevBoundingBox !== null) { - if (!bbox.containsBox(prevBoundingBox, viewsAndLayers.boundingBox)) { + if (!bbox.outerBoxcontainsInnerBox(prevBoundingBox, viewsAndLayers.boundingBox)) { setPrevBoundingBox(viewsAndLayers.boundingBox); } } else { diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts index fa1588561..d12d5123e 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts @@ -1,6 +1,7 @@ import { getWellTrajectoriesOptions, postGetPolylineIntersectionOptions } from "@api"; import { IntersectionReferenceSystem } from "@equinor/esv-intersection"; import * as bbox from "@lib/utils/boundingBox"; +import * as vec3 from "@lib/utils/vec3"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; @@ -60,12 +61,23 @@ export class IntersectionRealizationGridLayer return null; } - // TODO: Implement bounding box calculation - if (data) { - return null; + const boundingBox = bbox.create( + vec3.create(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY), + vec3.create(Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY) + ); + + for (const section of data.fenceMeshSections) { + boundingBox.min.x = Math.min(boundingBox.min.x, section.start_utm_x, section.end_utm_x); + boundingBox.max.x = Math.max(boundingBox.max.x, section.start_utm_x, section.end_utm_x); + boundingBox.min.y = Math.min(boundingBox.min.y, section.start_utm_y, section.end_utm_y); + boundingBox.max.y = Math.max(boundingBox.max.y, section.start_utm_y, section.end_utm_y); + for (let i = 2; i < section.verticesUzFloat32Arr.length; i += 3) { + boundingBox.min.z = Math.min(boundingBox.min.z, section.verticesUzFloat32Arr[i]); + boundingBox.max.z = Math.max(boundingBox.max.z, section.verticesUzFloat32Arr[i]); + } } - return null; + return boundingBox; } makeValueRange(): [number, number] | null { diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts index 8075ff4ea..88ad07d97 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts @@ -1,4 +1,6 @@ import { getGridParameterOptions, getGridSurfaceOptions } from "@api"; +import * as bbox from "@lib/utils/boundingBox"; +import * as vec3 from "@lib/utils/vec3"; import { GridMappedProperty_trans, GridSurface_trans, @@ -8,7 +10,7 @@ import { import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { QueryClient } from "@tanstack/react-query"; @@ -56,27 +58,20 @@ export class RealizationGridLayer implements Layer ({ x: point[0], y: point[1], z: data.u_min })); - cornerPoints.push(...data.bbox_utm.map((point) => ({ x: point[0], y: point[1], z: data.u_max }))); - - return fromCornerPoints(cornerPoints); + return bbox.create( + vec3.create(data.bbox_utm[0][0], data.bbox_utm[0][1], data.u_min), + vec3.create(data.bbox_utm[1][0], data.bbox_utm[1][1], data.u_max) + ); } - predictBoundingBox(): OBBox | null { + predictNextGeometryAndBoundingBox(): [Geometry, bbox.BBox] | null { const settings = this.getSettingsContext().getDelegate().getSettings(); const seismicCrosslineNumber = settings[SettingType.SEISMIC_CROSSLINE].getDelegate().getValue(); const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); @@ -104,18 +105,23 @@ export class RealizationSeismicCrosslineLayer const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); - const cornerPoints: Vec3[] = [ - { x: rotatedMinXY.x, y: rotatedMinXY.y, z: zmin }, - { x: rotatedMaxXY.x, y: rotatedMinXY.y, z: zmin }, - { x: rotatedMaxXY.x, y: rotatedMaxXY.y, z: zmin }, - { x: rotatedMinXY.x, y: rotatedMaxXY.y, z: zmin }, - { x: rotatedMinXY.x, y: rotatedMinXY.y, z: zmax }, - { x: rotatedMaxXY.x, y: rotatedMinXY.y, z: zmax }, - { x: rotatedMaxXY.x, y: rotatedMaxXY.y, z: zmax }, - { x: rotatedMinXY.x, y: rotatedMaxXY.y, z: zmax }, + const geometry: Geometry = { + type: GeometryType.POLYGON, + points: [ + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), + ], + }; + + return [ + geometry, + bbox.create( + vec3.create(Math.min(rotatedMinXY.x, rotatedMaxXY.x), Math.min(rotatedMinXY.y, rotatedMaxXY.y), zmin), + vec3.create(Math.max(rotatedMinXY.x, rotatedMaxXY.x), Math.max(rotatedMinXY.y, rotatedMaxXY.y), zmax) + ), ]; - - return fromCornerPoints(cornerPoints); } makeValueRange(): [number, number] | null { diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts index d85eb0c74..ae21a5434 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts @@ -1,7 +1,8 @@ import { getDepthSliceOptions } from "@api"; -import { OBBox, fromCornerPoints } from "@lib/utils/orientedBoundingBox"; +import * as bbox from "@lib/utils/boundingBox"; +import { Geometry, GeometryType } from "@lib/utils/geometry"; import { rotatePoint2Around } from "@lib/utils/vec2"; -import { Vec3 } from "@lib/utils/vec3"; +import * as vec3 from "@lib/utils/vec3"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; @@ -54,7 +55,7 @@ export class RealizationSeismicDepthSliceLayer return this._layerDelegate; } - makeBoundingBox(): OBBox | null { + makeBoundingBox(): bbox.BBox | null { const data = this._layerDelegate.getData(); if (!data) { return null; @@ -68,17 +69,21 @@ export class RealizationSeismicDepthSliceLayer } // The endpoint returns the bounding box as four points - - const cornerPoints: Vec3[] = data.bbox_utm.map((point) => { - return { x: point[0], y: point[1], z: seismicDepth }; - }); - - const obbox = fromCornerPoints(cornerPoints); - - return obbox; + return bbox.create( + vec3.create( + Math.min(...data.bbox_utm.map((point) => point[0])), + Math.min(...data.bbox_utm.map((point) => point[1])), + seismicDepth + ), + vec3.create( + Math.max(...data.bbox_utm.map((point) => point[0])), + Math.max(...data.bbox_utm.map((point) => point[1])), + seismicDepth + ) + ); } - predictBoundingBox(): OBBox | null { + predictNextGeometryAndBoundingBox(): [Geometry, bbox.BBox] | null { const settings = this.getSettingsContext().getDelegate().getSettings(); const seismicDepthSliceNumber = settings[SettingType.SEISMIC_DEPTH_SLICE].getDelegate().getValue(); const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); @@ -113,7 +118,22 @@ export class RealizationSeismicDepthSliceLayer const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); - return { x: [rotatedMinXY.x, rotatedMaxXY.x], y: [rotatedMinXY.y, rotatedMaxXY.y], z: [zmin, zmax] }; + const boundingBox = bbox.create( + vec3.create(Math.min(rotatedMinXY.x, rotatedMaxXY.x), Math.min(rotatedMinXY.y, rotatedMaxXY.y), zmin), + vec3.create(Math.max(rotatedMinXY.x, rotatedMaxXY.x), Math.max(rotatedMinXY.y, rotatedMaxXY.y), zmax) + ); + + const geometry: Geometry = { + type: GeometryType.POLYGON, + points: [ + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), + ], + }; + + return [geometry, boundingBox]; } doSettingsChangesRequireDataRefetch( diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts index 1cb6c3539..c99a616ba 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts @@ -1,8 +1,12 @@ import { getInlineSliceOptions } from "@api"; +import * as bbox from "@lib/utils/boundingBox"; +import { Geometry, GeometryType } from "@lib/utils/geometry"; +import { rotatePoint2Around } from "@lib/utils/vec2"; +import * as vec3 from "@lib/utils/vec3"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { QueryClient } from "@tanstack/react-query"; @@ -10,12 +14,18 @@ import { QueryClient } from "@tanstack/react-query"; import { isEqual } from "lodash"; import { RealizationSeismicInlineSettingsContext } from "./RealizationSeismicInlineSettingsContext"; -import { RealizationSeismicInlineSettings } from "./types"; +import { RealizationSeismicInlineSettings, RealizationSeismicInlineSliceStoredData } from "./types"; import { SeismicSliceData_trans, transformSeismicSlice } from "../../../settings/queries/queryDataTransforms"; -export class RealizationSeismicInlineLayer implements Layer { - private _layerDelegate: LayerDelegate; +export class RealizationSeismicInlineLayer + implements Layer +{ + private _layerDelegate: LayerDelegate< + RealizationSeismicInlineSettings, + SeismicSliceData_trans, + RealizationSeismicInlineSliceStoredData + >; private _itemDelegate: ItemDelegate; constructor(layerManager: LayerManager) { @@ -36,7 +46,11 @@ export class RealizationSeismicInlineLayer implements Layer { + getLayerDelegate(): LayerDelegate< + RealizationSeismicInlineSettings, + SeismicSliceData_trans, + RealizationSeismicInlineSliceStoredData + > { return this._layerDelegate; } @@ -47,17 +61,70 @@ export class RealizationSeismicInlineLayer implements Layer m.seismicAttribute === seismicAttribute && m.isoDateOrInterval === isoTimeOrInterval + ); + + if (!meta) { + return null; + } + + const xmin = meta.spec.xOrigin; + const xmax = meta.spec.xOrigin + meta.spec.xInc * (meta.spec.numCols - 1); + + const ymin = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * seismicInlineNumber; + const ymax = ymin; + + const zmin = meta.spec.zOrigin; + const zmax = meta.spec.zOrigin + meta.spec.zInc * meta.spec.zFlip * (meta.spec.numLayers - 1); + + const maxXY = { x: xmax, y: ymax }; + const minXY = { x: xmin, y: ymin }; + const origin = { x: meta.spec.xOrigin, y: meta.spec.yOrigin }; + + const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); + const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); + + const geometry: Geometry = { + type: GeometryType.POLYGON, + points: [ + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), + ], }; + + return [ + geometry, + bbox.create( + vec3.create(Math.min(rotatedMinXY.x, rotatedMaxXY.x), Math.min(rotatedMinXY.y, rotatedMaxXY.y), zmin), + vec3.create(Math.max(rotatedMinXY.x, rotatedMaxXY.x), Math.max(rotatedMinXY.y, rotatedMaxXY.y), zmax) + ), + ]; } makeValueRange(): [number, number] | null { diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts index e8a9296bb..0af7a367a 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts @@ -9,13 +9,21 @@ import { SeismicInlineSetting } from "@modules/_shared/LayerFramework/settings/i import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { RealizationSeismicInlineSettings } from "./types"; +import { RealizationSeismicInlineSettings, RealizationSeismicInlineSliceStoredData } from "./types"; -export class RealizationSeismicInlineSettingsContext implements SettingsContext { - private _contextDelegate: SettingsContextDelegate; +export class RealizationSeismicInlineSettingsContext + implements SettingsContext +{ + private _contextDelegate: SettingsContextDelegate< + RealizationSeismicInlineSettings, + RealizationSeismicInlineSliceStoredData + >; constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate(this, layerManager, { + this._contextDelegate = new SettingsContextDelegate< + RealizationSeismicInlineSettings, + RealizationSeismicInlineSliceStoredData + >(this, layerManager, { [SettingType.ENSEMBLE]: new EnsembleSetting(), [SettingType.REALIZATION]: new RealizationSetting(), [SettingType.ATTRIBUTE]: new AttributeSetting(), @@ -33,7 +41,7 @@ export class RealizationSeismicInlineSettingsContext implements SettingsContext< ); } - getDelegate(): SettingsContextDelegate { + getDelegate(): SettingsContextDelegate { return this._contextDelegate; } @@ -45,7 +53,7 @@ export class RealizationSeismicInlineSettingsContext implements SettingsContext< helperDependency, availableSettingsUpdater, queryClient, - }: DefineDependenciesArgs) { + }: DefineDependenciesArgs) { availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { const fieldIdentifier = getGlobalSetting("fieldId"); const ensembles = getGlobalSetting("ensembles"); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts index c03351f50..335e60d80 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts @@ -1,3 +1,4 @@ +import { SeismicCubeMeta_api } from "@api"; import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; @@ -8,3 +9,7 @@ export type RealizationSeismicInlineSettings = { [SettingType.TIME_OR_INTERVAL]: string | null; [SettingType.SEISMIC_INLINE]: number | null; }; + +export type RealizationSeismicInlineSliceStoredData = { + seismicCubeMeta: SeismicCubeMeta_api[]; +}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts index 0179b131b..07752bb32 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts @@ -1,9 +1,11 @@ import { SurfaceDataPng_api, SurfaceTimeType_api } from "@api"; import { getSurfaceDataOptions } from "@api"; +import * as bbox from "@lib/utils/boundingBox"; +import * as vec3 from "@lib/utils/vec3"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { FullSurfaceAddress, SurfaceAddressBuilder } from "@modules/_shared/Surface"; @@ -51,16 +53,15 @@ export class RealizationSurfaceLayer return !isEqual(prevSettings, newSettings); } - makeBoundingBox(): BoundingBox | null { + makeBoundingBox(): bbox.BBox | null { const data = this._layerDelegate.getData(); if (!data) { return null; } return { - x: [data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.max_x], - y: [data.transformed_bbox_utm.min_y, data.transformed_bbox_utm.max_y], - z: [0, 0], + min: vec3.create(data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.min_y, 0), + max: vec3.create(data.transformed_bbox_utm.max_x, data.transformed_bbox_utm.max_y, 0), }; } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index 98211124c..0c445f5e3 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -18,7 +18,6 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { colorScale, settings, isLoading, - predictedNextOrientedBoundingBox: predictedNextBoundingBox, }: VisualizationFunctionArgs): Layer { let bbox: number[][] = [ [data.bbox_utm[0][0], data.bbox_utm[0][1], data.u_min], @@ -36,6 +35,7 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { ]; } + /* if (isLoading && predictedNextBoundingBox) { if (plane === Plane.DEPTH) { bbox = [ @@ -53,6 +53,7 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { ]; } } + */ return new SeismicFenceMeshLayer({ id, diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 52d8f58f4..6e4365953 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -6,7 +6,7 @@ import { useViewStatusWriter } from "@framework/StatusWriter"; import { WorkbenchSession } from "@framework/WorkbenchSession"; import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { useElementSize } from "@lib/hooks/useElementSize"; -import { Rect3D, outerRectContainsInnerRect } from "@lib/utils/geometry"; +import * as bbox from "@lib/utils/boundingBox"; import { RealizationSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; @@ -23,7 +23,6 @@ import { makeSeismicFenceMeshLayerFunction, } from "@modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer"; import { LayerManager, LayerManagerTopic } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox } from "@modules/_shared/LayerFramework/interfaces"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; import { @@ -72,7 +71,7 @@ export type LayersWrapperProps = { }; export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { - const [prevBoundingBox, setPrevBoundingBox] = React.useState(null); + const [prevBoundingBox, setPrevBoundingBox] = React.useState(null); const mainDivRef = React.useRef(null); const mainDivSize = useElementSize(mainDivRef); @@ -151,25 +150,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { if (viewsAndLayers.boundingBox !== null) { if (prevBoundingBox !== null) { - const oldBoundingRect: Rect3D | null = { - x: prevBoundingBox.x[0], - y: prevBoundingBox.y[0], - z: prevBoundingBox.z[0], - width: prevBoundingBox.x[1] - prevBoundingBox.x[0], - height: prevBoundingBox.y[1] - prevBoundingBox.y[0], - depth: prevBoundingBox.z[1] - prevBoundingBox.z[0], - }; - - const newBoundingRect: Rect3D = { - x: viewsAndLayers.boundingBox.x[0], - y: viewsAndLayers.boundingBox.y[0], - z: viewsAndLayers.boundingBox.z[0], - width: viewsAndLayers.boundingBox.x[1] - viewsAndLayers.boundingBox.x[0], - height: viewsAndLayers.boundingBox.y[1] - viewsAndLayers.boundingBox.y[0], - depth: viewsAndLayers.boundingBox.z[1] - viewsAndLayers.boundingBox.z[0], - }; - - if (!outerRectContainsInnerRect(oldBoundingRect, newBoundingRect)) { + if (!bbox.outerBoxcontainsInnerBox(prevBoundingBox, viewsAndLayers.boundingBox)) { setPrevBoundingBox(viewsAndLayers.boundingBox); } } else { @@ -185,14 +166,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { let bounds: BoundingBox3D | undefined = undefined; if (prevBoundingBox) { - bounds = [ - prevBoundingBox.x[0], - prevBoundingBox.y[0], - prevBoundingBox.z[0], - prevBoundingBox.x[1], - prevBoundingBox.y[1], - prevBoundingBox.z[1], - ]; + bounds = bbox.toNumArray(prevBoundingBox); } const layers = viewerLayers.toSorted((a, b) => b.position - a.position).map((layer) => layer.layer); diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts index 83e45c602..8e9ee13e9 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts @@ -2,6 +2,7 @@ import { StatusMessage } from "@framework/ModuleInstanceStatusController"; import { ApiErrorHelper } from "@framework/utils/ApiErrorHelper"; import * as bbox from "@lib/utils/boundingBox"; import { isDevMode } from "@lib/utils/devMode"; +import { Geometry } from "@lib/utils/geometry"; import { QueryClient, isCancelledError } from "@tanstack/react-query"; import { SettingsContextDelegateTopic } from "./SettingsContextDelegate"; @@ -56,8 +57,9 @@ export class LayerDelegate> extends Item { getLayerDelegate(): LayerDelegate; + /** + * This method needs to be implemented by the layer. It should return true if the data needs to be refetched when the settings change. + * @param prevSettings The previous settings + * @param newSettings The new settings + */ doSettingsChangesRequireDataRefetch(prevSettings: TSettings, newSettings: TSettings): boolean; + /** + * This method needs to be implemented by the layer. It should fetch the data for the layer and return a promise to it. + */ fetchData(queryClient: QueryClient): Promise; + /** + * Implement this method if you want to provide an axis aligned bounding box for the layer. This can be used for adjusting the camera view. + */ makeBoundingBox?(): BBox | null; - predictBoundingBox?(): BBox | null; + /** + * Implement this method if you want to provide a predicted geometry and an respective axis aligned bounding box for the layer. The geometry can be used to preview the layer before the actual data is fetched. + */ + predictNextGeometryAndBoundingBox?(): [Geometry, BBox] | null; + /** + * Implement this method if you want to provide a value range for the layer. This can be used for adjusting the color scale. + */ makeValueRange?(): [number, number] | null; } diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesLayer.ts index e27bf3189..8f2a01d7e 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesLayer.ts @@ -1,6 +1,6 @@ import { WellboreTrajectory_api, getWellTrajectoriesOptions } from "@api"; -import { BBox } from "@lib/utils/boundingBox"; -import { OBBox, fromAxisAlignedBoundingBox } from "@lib/utils/orientedBoundingBox"; +import * as bbox from "@lib/utils/boundingBox"; +import * as vec3 from "@lib/utils/vec3"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; @@ -47,33 +47,33 @@ export class DrilledWellTrajectoriesLayer implements Layer { diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksLayer.ts index 6f9bb8c7c..e88f64884 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer/DrilledWellborePicksLayer.ts @@ -1,6 +1,6 @@ import { WellborePick_api, getWellborePicksForPickIdentifierOptions } from "@api"; -import { BBox } from "@lib/utils/boundingBox"; -import { OBBox, fromAxisAlignedBoundingBox } from "@lib/utils/orientedBoundingBox"; +import * as bbox from "@lib/utils/boundingBox"; +import * as vec3 from "@lib/utils/vec3"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; @@ -47,27 +47,27 @@ export class DrilledWellborePicksLayer implements Layer { diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts index 52832fee4..fa1588561 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts @@ -1,9 +1,10 @@ import { getWellTrajectoriesOptions, postGetPolylineIntersectionOptions } from "@api"; import { IntersectionReferenceSystem } from "@equinor/esv-intersection"; +import * as bbox from "@lib/utils/boundingBox"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { BoundingBox, Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; +import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; import { @@ -53,7 +54,7 @@ export class IntersectionRealizationGridLayer return !isEqual(prevSettings, newSettings); } - makeBoundingBox(): BoundingBox | null { + makeBoundingBox(): bbox.BBox | null { const data = this._layerDelegate.getData(); if (!data) { return null; diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts index 0b774a4ea..cdbf73c58 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts @@ -3,7 +3,8 @@ import { Layer as EsvLayer } from "@equinor/esv-intersection"; import { StatusMessage } from "@framework/ModuleInstanceStatusController"; import { defaultColorPalettes, defaultContinuousSequentialColorPalettes } from "@framework/utils/colorPalettes"; import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; -import * as obbox from "@lib/utils/orientedBoundingBox"; +import * as bbox from "@lib/utils/boundingBox"; +import { Geometry } from "@lib/utils/geometry"; import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName"; @@ -28,8 +29,9 @@ export type VisualizationFunctionArgs = { colorScale: ColorScaleWithName; settings: TSettings; isLoading: boolean; - orientedBoundingBox: obbox.OBBox | null; - predictedNextOrientedBoundingBox: obbox.OBBox | null; + boundingBox: bbox.BBox | null; + predictedBoundingBox: bbox.BBox | null; + predictedGeometry: Geometry | null; }; export type TargetReturnTypes = { @@ -58,7 +60,7 @@ export type FactoryProduct = { views: VisualizationView[]; layers: LayerWithPosition[]; errorMessages: (StatusMessage | string)[]; - boundingBox: obbox.OBBox | null; + boundingBox: bbox.BBox | null; colorScales: ColorScaleWithId[]; numLoadingLayers: number; }; @@ -86,11 +88,11 @@ export class VisualizationFactory { const collectedColorScales: ColorScaleWithId[] = []; const collectedErrorMessages: (StatusMessage | string)[] = []; let collectedNumLoadingLayers = 0; - let globalBoundingBox: obbox.OBBox | null = null; + let globalBoundingBox: bbox.BBox | null = null; const children = groupDelegate.getChildren(); - const maybeApplyBoundingBox = (boundingBox: obbox.OBBox | null) => { + const maybeApplyBoundingBox = (boundingBox: bbox.BBox | null) => { if (boundingBox) { globalBoundingBox = globalBoundingBox === null ? boundingBox : this.makeNewBoundingBox(boundingBox, globalBoundingBox); @@ -195,13 +197,14 @@ export class VisualizationFactory { colorScale, settings: layer.getLayerDelegate().getSettingsContext().getDelegate().getValues(), isLoading: layer.getLayerDelegate().getStatus() === LayerStatus.LOADING, - orientedBoundingBox: layer.getLayerDelegate().getBoundingBox(), - predictedNextOrientedBoundingBox: layer.getLayerDelegate().getPredictedBoundingBox(), + boundingBox: layer.getLayerDelegate().getBoundingBox(), + predictedBoundingBox: layer.getLayerDelegate().getPredictedBoundingBox(), + predictedGeometry: layer.getLayerDelegate().getPredictedGeometry(), }); } - private makeNewBoundingBox(newBoundingBox: obbox.OBBox, oldBoundingBox: obbox.OBBox): obbox.OBBox { - return obbox.combine(newBoundingBox, oldBoundingBox); + private makeNewBoundingBox(newBoundingBox: bbox.BBox, oldBoundingBox: bbox.BBox): bbox.BBox { + return bbox.combine(newBoundingBox, oldBoundingBox); } private findColorScale(layer: Layer): { id: string; colorScale: ColorScaleWithName } | null { diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts index edc912ea0..32f3e1c87 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts @@ -1,6 +1,5 @@ import { WellboreTrajectory_api } from "@api"; import * as bbox from "@lib/utils/boundingBox"; -import * as obbox from "@lib/utils/orientedBoundingBox"; import { Feature, FeatureCollection, GeoJsonProperties, GeometryCollection, LineString, Point } from "geojson"; @@ -57,7 +56,7 @@ export function makeWellsLayer({ id, data, name, - orientedBoundingBox, + boundingBox, }: VisualizationFunctionArgs): AdvancedWellsLayer { // Filter out some wellbores that are known to be not working - this is a temporary solution const filteredData = data.filter((wellbore) => wellbore.uniqueWellboreIdentifier !== "NO 34/4-K-3 AH"); @@ -68,9 +67,7 @@ export function makeWellsLayer({ features: filteredData.map((wellTrajectory) => wellTrajectoryToGeojson(wellTrajectory)), }; - const bbox3d = orientedBoundingBox - ? bbox.toNumArray(obbox.toAxisAlignedBoundingBox(orientedBoundingBox)) - : undefined; + const bbox3d = boundingBox ? bbox.toNumArray(boundingBox) : undefined; return new AdvancedWellsLayer({ id, From 3a2611abc7d82a2d704c1a93533f473938604e2d Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 20 Feb 2025 17:48:55 +0100 Subject: [PATCH 58/97] wip --- .../primary/services/vds_access/vds_access.py | 10 ++++++++-- .../makeRealizationSurfaceLayer.ts | 20 ++++++++++++++++--- .../SeismicFenceMeshLayer.ts | 6 ++++-- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/backend_py/primary/primary/services/vds_access/vds_access.py b/backend_py/primary/primary/services/vds_access/vds_access.py index c818f17b6..fc93677a7 100644 --- a/backend_py/primary/primary/services/vds_access/vds_access.py +++ b/backend_py/primary/primary/services/vds_access/vds_access.py @@ -94,8 +94,14 @@ async def get_inline_slice_async(self, line_no: int) -> Tuple[NDArray[np.float32 response = await self._query_async(endpoint, slice_request) parts = self._extract_and_validate_body_parts_from_response(response) - - metadata = VdsSliceMetadata(**json.loads(parts[0].content)) + response_metadata = json.loads(parts[0].content) + metadata = VdsSliceMetadata( + format=response_metadata["format"], + shape=response_metadata["shape"], + x_axis=VdsAxis(**response_metadata["x"]), + y_axis=VdsAxis(**response_metadata["y"]), + geospatial=response_metadata["geospatial"], + ) self._assert_valid_metadata_format_and_shape(metadata) byte_array = parts[1].content diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts index e6302e38f..c1f0fc07c 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts @@ -16,14 +16,28 @@ export function makeRealizationSurfaceLayer({ data, colorScale, }: VisualizationFunctionArgs): MapLayer { - if (!isSurfaceDataFloat(data)) { - throw new Error("SurfaceDataPng_api is not supported in makeRealizationSurfaceLayer"); + if (isSurfaceDataFloat(data)) { + return new MapLayer({ + id, + name, + meshData: data.valuesFloat32Arr, + frame: { + origin: [data.surface_def.origin_utm_x, data.surface_def.origin_utm_y], + count: [data.surface_def.npoints_x, data.surface_def.npoints_y], + increment: [data.surface_def.inc_x, data.surface_def.inc_y], + rotDeg: data.surface_def.rot_deg, + }, + valueRange: [data.value_min, data.value_max], + colorMapRange: [data.value_min, data.value_max], + colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max), + gridLines: false, + }); } return new MapLayer({ id, name, - meshData: data.valuesFloat32Arr, + meshData: data.png_image_base64, frame: { origin: [data.surface_def.origin_utm_x, data.surface_def.origin_utm_y], count: [data.surface_def.npoints_x, data.surface_def.npoints_y], diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts index 9238d813a..10ff5246d 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts @@ -134,14 +134,16 @@ export class SeismicFenceMeshLayer extends CompositeLayer Date: Fri, 21 Feb 2025 12:22:09 +0100 Subject: [PATCH 59/97] wip --- frontend/src/lib/utils/boundingBox.ts | 14 +++++++-- frontend/src/lib/utils/geometry.ts | 14 ++++++--- .../RealizationSeismicCrosslineLayer.ts | 30 ++++++++++--------- .../RealizationSeismicDepthSliceLayer.ts | 23 ++++++++------ .../RealizationSeismicInlineLayer.ts | 30 ++++++++++--------- .../LayerFramework/delegates/LayerDelegate.ts | 11 ++----- .../_shared/LayerFramework/interfaces.ts | 2 +- .../visualization/VisualizationFactory.ts | 2 -- .../PreviewLayer/PreviewLayer.ts | 0 .../PreviewLayer/_private/PolygonLayer.ts | 0 10 files changed, 72 insertions(+), 54 deletions(-) create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/PolygonLayer.ts diff --git a/frontend/src/lib/utils/boundingBox.ts b/frontend/src/lib/utils/boundingBox.ts index 15dc92f01..b71dddf23 100644 --- a/frontend/src/lib/utils/boundingBox.ts +++ b/frontend/src/lib/utils/boundingBox.ts @@ -8,8 +8,11 @@ export type BBox = { }; /** - Creates a new bounding box. - */ + * Creates a new bounding box. + * @param min The minimum point of the bounding box. + * @param max The maximum point of the bounding box. + * @returns A new bounding box. + */ export function create(min: vec3.Vec3, max: vec3.Vec3): BBox { return { min, max }; } @@ -21,6 +24,13 @@ export function fromBoundingBox3DApi(boundingBox: BoundingBox3D_api): BBox { ); } +export function fromBoundingBox2DApi(boundingBox: BoundingBox3D_api): BBox { + return create( + vec3.create(boundingBox.xmin, boundingBox.ymin, 0), + vec3.create(boundingBox.xmax, boundingBox.ymax, 0) + ); +} + /** Returns true if the bounding box contains the given point. */ diff --git a/frontend/src/lib/utils/geometry.ts b/frontend/src/lib/utils/geometry.ts index e2473b1b2..8f94b74f0 100644 --- a/frontend/src/lib/utils/geometry.ts +++ b/frontend/src/lib/utils/geometry.ts @@ -1,3 +1,4 @@ +import { BBox } from "./boundingBox"; import type { Vec2 } from "./vec2"; import { Vec3 } from "./vec3"; @@ -22,22 +23,27 @@ export type Rect3D = { depth: number; }; -export enum GeometryType { +export enum ShapeType { POLYGON = "polygon", ELEVATED_POLYGON = "elevated_polygon", } -export type Geometry = +export type Shape = | { - type: GeometryType.POLYGON; + type: ShapeType.POLYGON; points: Vec3[]; } | { - type: GeometryType.ELEVATED_POLYGON; + type: ShapeType.ELEVATED_POLYGON; points: Vec3[]; height: number; }; +export type Geometry = { + shapes: Shape[]; + boundingBox: BBox; +}; + export const ORIGIN = Object.freeze({ x: 0, y: 0 }); export const MANHATTAN_LENGTH = 13.11; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index db91b6b49..c906e7b62 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -1,6 +1,6 @@ import { getCrosslineSliceOptions } from "@api"; import * as bbox from "@lib/utils/boundingBox"; -import { Geometry, GeometryType } from "@lib/utils/geometry"; +import { Geometry, ShapeType } from "@lib/utils/geometry"; import { rotatePoint2Around } from "@lib/utils/vec2"; import * as vec3 from "@lib/utils/vec3"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; @@ -70,7 +70,7 @@ export class RealizationSeismicCrosslineLayer ); } - predictNextGeometryAndBoundingBox(): [Geometry, bbox.BBox] | null { + predictNextGeometry(): Geometry | null { const settings = this.getSettingsContext().getDelegate().getSettings(); const seismicCrosslineNumber = settings[SettingType.SEISMIC_CROSSLINE].getDelegate().getValue(); const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); @@ -106,22 +106,24 @@ export class RealizationSeismicCrosslineLayer const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); const geometry: Geometry = { - type: GeometryType.POLYGON, - points: [ - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), + shapes: [ + { + type: ShapeType.POLYGON, + points: [ + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), + ], + }, ], - }; - - return [ - geometry, - bbox.create( + boundingBox: bbox.create( vec3.create(Math.min(rotatedMinXY.x, rotatedMaxXY.x), Math.min(rotatedMinXY.y, rotatedMaxXY.y), zmin), vec3.create(Math.max(rotatedMinXY.x, rotatedMaxXY.x), Math.max(rotatedMinXY.y, rotatedMaxXY.y), zmax) ), - ]; + }; + + return geometry; } makeValueRange(): [number, number] | null { diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts index ae21a5434..ddcdb5f4a 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts @@ -1,6 +1,6 @@ import { getDepthSliceOptions } from "@api"; import * as bbox from "@lib/utils/boundingBox"; -import { Geometry, GeometryType } from "@lib/utils/geometry"; +import { Geometry, ShapeType } from "@lib/utils/geometry"; import { rotatePoint2Around } from "@lib/utils/vec2"; import * as vec3 from "@lib/utils/vec3"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; @@ -83,7 +83,7 @@ export class RealizationSeismicDepthSliceLayer ); } - predictNextGeometryAndBoundingBox(): [Geometry, bbox.BBox] | null { + predictNextGeometry(): Geometry | null { const settings = this.getSettingsContext().getDelegate().getSettings(); const seismicDepthSliceNumber = settings[SettingType.SEISMIC_DEPTH_SLICE].getDelegate().getValue(); const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); @@ -124,16 +124,21 @@ export class RealizationSeismicDepthSliceLayer ); const geometry: Geometry = { - type: GeometryType.POLYGON, - points: [ - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), + shapes: [ + { + type: ShapeType.POLYGON, + points: [ + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), + ], + }, ], + boundingBox, }; - return [geometry, boundingBox]; + return geometry; } doSettingsChangesRequireDataRefetch( diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts index c99a616ba..133f3b48c 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts @@ -1,6 +1,6 @@ import { getInlineSliceOptions } from "@api"; import * as bbox from "@lib/utils/boundingBox"; -import { Geometry, GeometryType } from "@lib/utils/geometry"; +import { Geometry, ShapeType } from "@lib/utils/geometry"; import { rotatePoint2Around } from "@lib/utils/vec2"; import * as vec3 from "@lib/utils/vec3"; import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; @@ -73,7 +73,7 @@ export class RealizationSeismicInlineLayer ); } - predictNextGeometryAndBoundingBox(): [Geometry, bbox.BBox] | null { + predictNextGeometry(): Geometry | null { const settings = this.getSettingsContext().getDelegate().getSettings(); const seismicInlineNumber = settings[SettingType.SEISMIC_INLINE].getDelegate().getValue(); const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); @@ -109,22 +109,24 @@ export class RealizationSeismicInlineLayer const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); const geometry: Geometry = { - type: GeometryType.POLYGON, - points: [ - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), + shapes: [ + { + type: ShapeType.POLYGON, + points: [ + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), + vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), + vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), + ], + }, ], - }; - - return [ - geometry, - bbox.create( + boundingBox: bbox.create( vec3.create(Math.min(rotatedMinXY.x, rotatedMaxXY.x), Math.min(rotatedMinXY.y, rotatedMaxXY.y), zmin), vec3.create(Math.max(rotatedMinXY.x, rotatedMaxXY.x), Math.max(rotatedMinXY.y, rotatedMaxXY.y), zmax) ), - ]; + }; + + return geometry; } makeValueRange(): [number, number] | null { diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts index 8e9ee13e9..461efd47d 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/LayerDelegate.ts @@ -59,7 +59,6 @@ export class LayerDelegate = { settings: TSettings; isLoading: boolean; boundingBox: bbox.BBox | null; - predictedBoundingBox: bbox.BBox | null; predictedGeometry: Geometry | null; }; @@ -198,7 +197,6 @@ export class VisualizationFactory { settings: layer.getLayerDelegate().getSettingsContext().getDelegate().getValues(), isLoading: layer.getLayerDelegate().getStatus() === LayerStatus.LOADING, boundingBox: layer.getLayerDelegate().getBoundingBox(), - predictedBoundingBox: layer.getLayerDelegate().getPredictedBoundingBox(), predictedGeometry: layer.getLayerDelegate().getPredictedGeometry(), }); } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer.ts new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/PolygonLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/PolygonLayer.ts new file mode 100644 index 000000000..e69de29bb From 114781ebd3448c8b3dbda6095e6868221f032395 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 25 Feb 2025 01:19:56 +0100 Subject: [PATCH 60/97] wip --- .../Content/private-components/layout.tsx | 6 +- frontend/src/lib/utils/colorConstants.ts | 11 + frontend/src/lib/utils/geometry.ts | 28 +- frontend/src/lib/utils/vec2.ts | 2 +- frontend/src/lib/utils/vec3.ts | 12 + .../RealizationSeismicCrosslineLayer.ts | 25 +- .../RealizationSeismicDepthSliceLayer.ts | 29 +- .../RealizationSeismicInlineLayer.ts | 33 +- ...RealizationSeismicInlineSettingsContext.ts | 11 + .../makeSeismicFenceMeshLayer.ts | 22 +- .../deckgl/makeWellborePicksLayer.ts | 1 + .../visualization/deckgl/makeWellsLayer.ts | 35 +- .../PreviewLayer/PreviewLayer.ts | 51 ++ .../PreviewLayer/_private/PolygonLayer.ts | 0 .../PreviewLayer/_private/RectangleLayer.ts | 105 ++++ .../SeismicFenceMeshLayer.ts | 76 +-- .../customDeckGlLayers/WellborePicksLayer.ts | 166 +++---- .../WellsLayer/WellsLayer.ts | 49 ++ .../WellsLayer/_private/Line.ts | 32 ++ .../WellsLayer/_private/Mat3.ts | 113 +++++ .../WellsLayer/_private/Mat4.ts | 465 ++++++++++++++++++ .../WellsLayer/_private/Pipe.ts | 105 ++++ .../WellsLayer/_private/PipeLayer.ts | 171 +++++++ .../WellsLayer/_private/Plane.ts | 64 +++ .../_private/shaders/fragmentShader.glsl | 19 + .../_private/shaders/vertexShader.glsl | 27 + 26 files changed, 1441 insertions(+), 217 deletions(-) create mode 100644 frontend/src/lib/utils/colorConstants.ts delete mode 100644 frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/PolygonLayer.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/RectangleLayer.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Line.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Mat3.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Mat4.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Pipe.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Plane.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl diff --git a/frontend/src/framework/internal/components/Content/private-components/layout.tsx b/frontend/src/framework/internal/components/Content/private-components/layout.tsx index d2f7e8386..09aba79aa 100644 --- a/frontend/src/framework/internal/components/Content/private-components/layout.tsx +++ b/frontend/src/framework/internal/components/Content/private-components/layout.tsx @@ -15,7 +15,7 @@ import { } from "@lib/utils/geometry"; import { Vec2, - multiplyVec2, + multiplyElementWiseVec2, point2Distance, scaleVec2NonUniform, subtractVec2, @@ -95,7 +95,7 @@ export const Layout: React.FC = (props) => { setPosition( subtractVec2( relativePointerPosition, - multiplyVec2(relativePointerToElementDiff, { + multiplyElementWiseVec2(relativePointerToElementDiff, { x: draggedElementSize.width, y: 1, }) @@ -197,7 +197,7 @@ export const Layout: React.FC = (props) => { setPosition( subtractVec2( relativePointerPosition, - multiplyVec2(relativePointerToElementDiff, { + multiplyElementWiseVec2(relativePointerToElementDiff, { x: draggedElementSize.width, y: 1, }) diff --git a/frontend/src/lib/utils/colorConstants.ts b/frontend/src/lib/utils/colorConstants.ts new file mode 100644 index 000000000..98f7748b9 --- /dev/null +++ b/frontend/src/lib/utils/colorConstants.ts @@ -0,0 +1,11 @@ +// This is used to set colors for different states throughout the application where CSS properties are not accessible (e.g. WebGL). +// However, it should be in sync with what is set in Tailwind CSS. +// The location of this file can still be decided upon. +export type Colors = { + hover: [number, number, number]; + selected: [number, number, number]; +}; +export const COLORS: Colors = { + hover: [191, 219, 254], + selected: [37, 99, 235], +}; diff --git a/frontend/src/lib/utils/geometry.ts b/frontend/src/lib/utils/geometry.ts index 8f94b74f0..563a6289e 100644 --- a/frontend/src/lib/utils/geometry.ts +++ b/frontend/src/lib/utils/geometry.ts @@ -24,20 +24,20 @@ export type Rect3D = { }; export enum ShapeType { - POLYGON = "polygon", - ELEVATED_POLYGON = "elevated_polygon", -} - -export type Shape = - | { - type: ShapeType.POLYGON; - points: Vec3[]; - } - | { - type: ShapeType.ELEVATED_POLYGON; - points: Vec3[]; - height: number; - }; + RECTANGLE = "rectangle", +} + +export type Shape = { + type: ShapeType.RECTANGLE; + centerPoint: Vec3; + dimensions: Size2D; + normalizedEdgeVectors: { + // along width + u: Vec3; + // along height + v: Vec3; + }; +}; export type Geometry = { shapes: Shape[]; diff --git a/frontend/src/lib/utils/vec2.ts b/frontend/src/lib/utils/vec2.ts index d07b0a03a..9d10e6db1 100644 --- a/frontend/src/lib/utils/vec2.ts +++ b/frontend/src/lib/utils/vec2.ts @@ -40,7 +40,7 @@ export function scaleVec2NonUniform(vector: Vec2, scalarX: number, scalarY: numb return { x: vector.x * scalarX, y: vector.y * scalarY }; } -export function multiplyVec2(vecA: Vec2, vecB: Vec2): Vec2 { +export function multiplyElementWiseVec2(vecA: Vec2, vecB: Vec2): Vec2 { return { x: vecA.x * vecB.x, y: vecA.y * vecB.y }; } diff --git a/frontend/src/lib/utils/vec3.ts b/frontend/src/lib/utils/vec3.ts index d1a07d545..d321614df 100644 --- a/frontend/src/lib/utils/vec3.ts +++ b/frontend/src/lib/utils/vec3.ts @@ -14,6 +14,10 @@ export function fromArray(array: ArrayLike | [number, number, number]): return { x: array[0], y: array[1], z: array[2] }; } +export function toArray(vector: Vec3): [number, number, number] { + return [vector.x, vector.y, vector.z]; +} + export function length(vector: Vec3): number { return Math.sqrt(vector.x ** 2 + vector.y ** 2 + vector.z ** 2); } @@ -43,6 +47,14 @@ export function scale(vector: Vec3, scalar: number): Vec3 { return { x: vector.x * scalar, y: vector.y * scalar, z: vector.z * scalar }; } +export function scaleNonUniform(vector: Vec3, scalarX: number, scalarY: number, scalarZ: number): Vec3 { + return { x: vector.x * scalarX, y: vector.y * scalarY, z: vector.z * scalarZ }; +} + +export function multiplyElementWise(vecA: Vec3, vecB: Vec3): Vec3 { + return { x: vecA.x * vecB.x, y: vecA.y * vecB.y, z: vecA.z * vecB.z }; +} + export function equal(vector1: Vec3, vector2: Vec3): boolean { return vector1.x === vector2.x && vector1.y === vector2.y && vector1.z === vector2.z; } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts index c906e7b62..ca66f7fe4 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts @@ -108,13 +108,24 @@ export class RealizationSeismicCrosslineLayer const geometry: Geometry = { shapes: [ { - type: ShapeType.POLYGON, - points: [ - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), - ], + type: ShapeType.RECTANGLE, + centerPoint: vec3.create( + (rotatedMinXY.x + rotatedMaxXY.x) / 2, + (rotatedMinXY.y + rotatedMaxXY.y) / 2, + (zmin + zmax) / 2 + ), + dimensions: { + width: vec3.length( + vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0) + ), + height: Math.abs(zmax - zmin), + }, + normalizedEdgeVectors: { + u: vec3.normalize( + vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0) + ), + v: vec3.create(0, 0, 1), + }, }, ], boundingBox: bbox.create( diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts index ddcdb5f4a..2b96487be 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts @@ -108,31 +108,36 @@ export class RealizationSeismicDepthSliceLayer const ymin = meta.spec.yOrigin; const ymax = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * (meta.spec.numRows - 1); - const zmin = -seismicDepthSliceNumber; + const zmin = seismicDepthSliceNumber; const zmax = zmin; const maxXY = { x: xmax, y: ymax }; - const minXY = { x: xmin, y: ymin }; + const maxX = { x: xmax, y: ymin }; + const maxY = { x: xmin, y: ymax }; const origin = { x: meta.spec.xOrigin, y: meta.spec.yOrigin }; - const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); + const rotatedMaxX = rotatePoint2Around(maxX, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); + const rotatedMaxY = rotatePoint2Around(maxY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); const boundingBox = bbox.create( - vec3.create(Math.min(rotatedMinXY.x, rotatedMaxXY.x), Math.min(rotatedMinXY.y, rotatedMaxXY.y), zmin), - vec3.create(Math.max(rotatedMinXY.x, rotatedMaxXY.x), Math.max(rotatedMinXY.y, rotatedMaxXY.y), zmax) + vec3.create(Math.min(origin.x, rotatedMaxXY.x), Math.min(origin.y, rotatedMaxXY.y), zmin), + vec3.create(Math.max(origin.x, rotatedMaxXY.x), Math.max(origin.y, rotatedMaxXY.y), zmax) ); const geometry: Geometry = { shapes: [ { - type: ShapeType.POLYGON, - points: [ - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), - ], + type: ShapeType.RECTANGLE, + centerPoint: vec3.create((origin.x + rotatedMaxXY.x) / 2, (origin.y + rotatedMaxXY.y) / 2, zmin), + dimensions: { + width: Math.abs(rotatedMaxX.x - origin.x), + height: Math.abs(rotatedMaxY.y - origin.y), + }, + normalizedEdgeVectors: { + u: vec3.normalize(vec3.create(rotatedMaxX.x - origin.x, rotatedMaxX.y - origin.y, 0)), + v: vec3.normalize(vec3.create(rotatedMaxY.x - origin.x, rotatedMaxY.y - origin.y, 0)), + }, }, ], boundingBox, diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts index 133f3b48c..67aa26f9e 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts @@ -92,11 +92,11 @@ export class RealizationSeismicInlineLayer return null; } - const xmin = meta.spec.xOrigin; - const xmax = meta.spec.xOrigin + meta.spec.xInc * (meta.spec.numCols - 1); + const xmin = meta.spec.xOrigin + meta.spec.yInc * seismicInlineNumber; + const xmax = xmin; - const ymin = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * seismicInlineNumber; - const ymax = ymin; + const ymin = meta.spec.yOrigin; + const ymax = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * (meta.spec.numRows - 1); const zmin = meta.spec.zOrigin; const zmax = meta.spec.zOrigin + meta.spec.zInc * meta.spec.zFlip * (meta.spec.numLayers - 1); @@ -111,13 +111,24 @@ export class RealizationSeismicInlineLayer const geometry: Geometry = { shapes: [ { - type: ShapeType.POLYGON, - points: [ - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmin), - vec3.create(rotatedMaxXY.x, rotatedMaxXY.y, zmax), - vec3.create(rotatedMinXY.x, rotatedMinXY.y, zmax), - ], + type: ShapeType.RECTANGLE, + centerPoint: vec3.create( + (rotatedMinXY.x + rotatedMaxXY.x) / 2, + (rotatedMinXY.y + rotatedMaxXY.y) / 2, + (zmin + zmax) / 2 + ), + dimensions: { + width: vec3.length( + vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0) + ), + height: Math.abs(zmax - zmin), + }, + normalizedEdgeVectors: { + u: vec3.normalize( + vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0) + ), + v: vec3.create(0, 0, 1), + }, }, ], boundingBox: bbox.create( diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts index 0af7a367a..61b1d2cea 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts @@ -52,6 +52,7 @@ export class RealizationSeismicInlineSettingsContext defineDependencies({ helperDependency, availableSettingsUpdater, + storedDataUpdater, queryClient, }: DefineDependenciesArgs) { availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { @@ -96,6 +97,16 @@ export class RealizationSeismicInlineSettingsContext }); }); + storedDataUpdater("seismicCubeMeta", ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSeismicInlineDataDep); + + if (!data) { + return null; + } + + return data; + }); + availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { const data = getHelperDependency(realizationSeismicInlineDataDep); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index 0c445f5e3..eab9c49cc 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -18,6 +18,7 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { colorScale, settings, isLoading, + predictedGeometry, }: VisualizationFunctionArgs): Layer { let bbox: number[][] = [ [data.bbox_utm[0][0], data.bbox_utm[0][1], data.u_min], @@ -35,26 +36,6 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { ]; } - /* - if (isLoading && predictedNextBoundingBox) { - if (plane === Plane.DEPTH) { - bbox = [ - [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], settings.seismicDepthSlice], - [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], settings.seismicDepthSlice], - [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], settings.seismicDepthSlice], - [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], settings.seismicDepthSlice], - ]; - } else { - bbox = [ - [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], predictedNextBoundingBox.z[0]], - [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], predictedNextBoundingBox.z[0]], - [predictedNextBoundingBox.x[0], predictedNextBoundingBox.y[0], predictedNextBoundingBox.z[1]], - [predictedNextBoundingBox.x[1], predictedNextBoundingBox.y[1], predictedNextBoundingBox.z[1]], - ]; - } - } - */ - return new SeismicFenceMeshLayer({ id, name, @@ -71,6 +52,7 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max, false), zIncreaseDownwards: true, isLoading, + loadingGeometry: predictedGeometry ?? undefined, }); }; } diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer.ts index a402295b9..8774759b2 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer.ts @@ -22,5 +22,6 @@ export function makeWellborePicksLayer({ id, data: wellPicksData, pickable: true, + zIncreaseDownwards: true, }); } diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts index 32f3e1c87..188b92293 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts @@ -1,9 +1,9 @@ import { WellboreTrajectory_api } from "@api"; import * as bbox from "@lib/utils/boundingBox"; +import { WellsLayer, WellsLayerData } from "@modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer"; -import { Feature, FeatureCollection, GeoJsonProperties, GeometryCollection, LineString, Point } from "geojson"; +import { Feature, GeoJsonProperties, GeometryCollection, LineString, Point } from "geojson"; -import { AdvancedWellsLayer } from "../../../customDeckGlLayers/AdvancedWellsLayer"; import { VisualizationFunctionArgs } from "../VisualizationFactory"; function wellTrajectoryToGeojson( @@ -52,6 +52,7 @@ function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { return coords; } +/* export function makeWellsLayer({ id, data, @@ -84,4 +85,34 @@ export function makeWellsLayer({ }, boundingBox: bbox3d, }); +} + */ +export function makeWellsLayer({ + id, + data, + name, + boundingBox, +}: VisualizationFunctionArgs): WellsLayer { + // Filter out some wellbores that are known to be not working - this is a temporary solution + const filteredData = data.filter((wellbore) => wellbore.uniqueWellboreIdentifier !== "NO 34/4-K-3 AH"); + + const wellsData: WellsLayerData = filteredData.map((wellTrajectory) => { + return { + coordinates: zipCoords(wellTrajectory.eastingArr, wellTrajectory.northingArr, wellTrajectory.tvdMslArr) as [ + number, + number, + number + ][], + properties: { uuid: wellTrajectory.wellboreUuid, name: wellTrajectory.uniqueWellboreIdentifier }, + }; + }); + + const bbox3d = boundingBox ? bbox.toNumArray(boundingBox) : undefined; + + return new WellsLayer({ + id, + data: wellsData, + name, + boundingBox: bbox3d, + }); } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer.ts index e69de29bb..28beb30b2 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer.ts @@ -0,0 +1,51 @@ +import { CompositeLayer, Layer, LayersList } from "@deck.gl/core"; +import { Geometry, ShapeType } from "@lib/utils/geometry"; +import * as vec3 from "@lib/utils/vec3"; + +import { RectangleLayer } from "./_private/RectangleLayer"; + +export type PreviewLayerProps = { + id: string; + data: { + geometry: Geometry; + }; + zIncreaseDownwards?: boolean; +}; + +export class PreviewLayer extends CompositeLayer { + static layerName = "PreviewLayer"; + + renderLayers(): LayersList { + const { data } = this.props; + + const layers: Layer[] = []; + + const zFactor = this.props.zIncreaseDownwards ? -1 : 1; + + for (const [idx, shape] of data.geometry.shapes.entries()) { + if (shape.type === ShapeType.RECTANGLE) { + layers.push( + new RectangleLayer({ + id: `${idx}`, + data: { + centerPoint: vec3.toArray( + vec3.multiplyElementWise(shape.centerPoint, vec3.create(1, 1, zFactor)) + ), + dimensions: [shape.dimensions.width, shape.dimensions.height], + normalizedEdgeVectors: [ + vec3.toArray( + vec3.multiplyElementWise(shape.normalizedEdgeVectors.u, vec3.create(1, 1, zFactor)) + ), + vec3.toArray( + vec3.multiplyElementWise(shape.normalizedEdgeVectors.v, vec3.create(1, 1, zFactor)) + ), + ], + }, + }) + ); + } + } + + return layers; + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/PolygonLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/PolygonLayer.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/RectangleLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/RectangleLayer.ts new file mode 100644 index 000000000..fac0a0823 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/RectangleLayer.ts @@ -0,0 +1,105 @@ +import { CompositeLayer, CompositeLayerProps, Layer, UpdateParameters } from "@deck.gl/core"; +import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; +import { Geometry } from "@luma.gl/engine"; + +export type RectangleLayerData = { + centerPoint: [number, number, number]; + dimensions: [number, number]; + normalizedEdgeVectors: [[number, number, number], [number, number, number]]; +}; + +export type RectangleLayerProps = { + id: string; + data: RectangleLayerData; +}; + +export class RectangleLayer extends CompositeLayer { + static layerName = "RectangleLayer"; + + // @ts-expect-error - private + state!: { + geometry: Geometry; + }; + + private makeGeometry(): Geometry { + const { data } = this.props; + + const vertices: Float32Array = new Float32Array(4 * 3); + const indices: Uint16Array = new Uint16Array(6); + + const [centerX, centerY, centerZ] = data.centerPoint; + const [width, height] = data.dimensions; + const [[uX, uY, uZ], [vX, vY, vZ]] = data.normalizedEdgeVectors; + + const halfWidth = width / 2; + const halfHeight = height / 2; + + // bottom left + vertices[0] = centerX - uX * halfWidth - vX * halfHeight; + vertices[1] = centerY - uY * halfWidth - vY * halfHeight; + vertices[2] = centerZ - uZ * halfWidth - vZ * halfHeight; + + // bottom right + vertices[3] = centerX + uX * halfWidth - vX * halfHeight; + vertices[4] = centerY + uY * halfWidth - vY * halfHeight; + vertices[5] = centerZ + uZ * halfWidth - vZ * halfHeight; + + // top right + vertices[6] = centerX + uX * halfWidth + vX * halfHeight; + vertices[7] = centerY + uY * halfWidth + vY * halfHeight; + vertices[8] = centerZ + uZ * halfWidth + vZ * halfHeight; + + // top left + vertices[9] = centerX - uX * halfWidth + vX * halfHeight; + vertices[10] = centerY - uY * halfWidth + vY * halfHeight; + vertices[11] = centerZ - uZ * halfWidth + vZ * halfHeight; + + // bottom left + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + + // top right + indices[3] = 2; + indices[4] = 3; + indices[5] = 0; + + return new Geometry({ + topology: "triangle-list", + attributes: { + positions: vertices, + }, + indices, + }); + } + + initializeState(): void { + this.setState({ + ...this.state, + isHovered: false, + isLoaded: false, + }); + } + + updateState({ changeFlags }: UpdateParameters>>) { + if (changeFlags.dataChanged) { + this.setState({ + geometry: this.makeGeometry(), + }); + } + } + + renderLayers() { + return [ + new SimpleMeshLayer({ + id: "mesh", + data: [0], + mesh: this.state.geometry, + getPosition: (d) => [0, 0, 0], + getColor: [100, 100, 100, 100], + material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, + pickable: false, + }), + ]; + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts index 10ff5246d..5d00997fa 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts @@ -6,7 +6,7 @@ import { PickingInfo, UpdateParameters, } from "@deck.gl/core"; -import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; +import { Geometry as LoadingGeometry } from "@lib/utils/geometry"; import { Geometry } from "@luma.gl/engine"; import { ExtendedLayerProps } from "@webviz/subsurface-viewer"; import { BoundingBox3D, ReportBoundingBoxAction } from "@webviz/subsurface-viewer/dist/components/Map"; @@ -17,6 +17,8 @@ import workerpool from "workerpool"; import { ExtendedSimpleMeshLayer } from "./_private/ExtendedSimpleMeshLayer"; import { WebworkerParameters, makeMesh } from "./_private/worker"; +import { PreviewLayer } from "../PreviewLayer/PreviewLayer"; + export type SeismicFenceMeshLayerPickingInfo = { properties?: { name: string; value: number }[]; } & PickingInfo; @@ -36,6 +38,7 @@ export interface SeismicFenceMeshLayerProps extends ExtendedLayerProps { hoverable?: boolean; zIncreaseDownwards?: boolean; isLoading?: boolean; + loadingGeometry?: LoadingGeometry; // Non public properties: reportBoundingBox?: React.Dispatch; @@ -311,54 +314,23 @@ export class SeismicFenceMeshLayer extends CompositeLayer[] = []; - if (isLoading || !isLoaded) { - for (const section of data.sections) { - const vertices = new Float32Array(4 * 3); - const indices = new Uint32Array([0, 1, 2, 2, 3, 0]); - - let verticesIndex = 0; - vertices[verticesIndex++] = section.boundingBox[0][0]; - vertices[verticesIndex++] = section.boundingBox[0][1]; - vertices[verticesIndex++] = (zIncreaseDownwards ? -1 : 1) * section.boundingBox[0][2]; - - vertices[verticesIndex++] = section.boundingBox[1][0]; - vertices[verticesIndex++] = section.boundingBox[1][1]; - vertices[verticesIndex++] = (zIncreaseDownwards ? -1 : 1) * section.boundingBox[1][2]; - - vertices[verticesIndex++] = section.boundingBox[3][0]; - vertices[verticesIndex++] = section.boundingBox[3][1]; - vertices[verticesIndex++] = (zIncreaseDownwards ? -1 : 1) * section.boundingBox[3][2]; - - vertices[verticesIndex++] = section.boundingBox[2][0]; - vertices[verticesIndex++] = section.boundingBox[2][1]; - vertices[verticesIndex++] = (zIncreaseDownwards ? -1 : 1) * section.boundingBox[2][2]; - - const placeholderGeometry = new Geometry({ - attributes: { - positions: vertices, + if ((isLoading || !isLoaded) && loadingGeometry) { + layers.push( + new PreviewLayer({ + id: "seismic-fence-mesh-layer-loading", + data: { + geometry: loadingGeometry, }, - topology: "triangle-list", - indices, - }); - layers.push( - new SimpleMeshLayer({ - id: "seismic-fence-mesh-layer-loading", - data: [0], - mesh: placeholderGeometry, - getPosition: [0, 0, 0], - getColor: [100, 100, 100, 100], - material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, - pickable: false, - }) - ); - } + zIncreaseDownwards, + }) + ); } else { layers.push( new ExtendedSimpleMeshLayer({ @@ -373,26 +345,6 @@ export class SeismicFenceMeshLayer extends CompositeLayer d, - getColor: [0, 0, 255, 255], - getWidth: 2, - pickable: false, - billboard: true, - widthUnits: "pixels", - parameters: { - depthTest: false, - }, - }) - ); - } - */ - return layers; } } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts index 6f09a63d8..c732ad2f5 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts @@ -1,9 +1,18 @@ -import { CompositeLayer, CompositeLayerProps, FilterContext, Layer, UpdateParameters } from "@deck.gl/core"; -import { GeoJsonLayer } from "@deck.gl/layers"; +import { + CompositeLayer, + CompositeLayerProps, + FilterContext, + Layer, + LayerContext, + PickingInfo, + UpdateParameters, +} from "@deck.gl/core"; +import { PointCloudLayer } from "@deck.gl/layers"; +import { COLORS } from "@lib/utils/colorConstants"; import { ExtendedLayerProps } from "@webviz/subsurface-viewer"; import { BoundingBox3D, ReportBoundingBoxAction } from "@webviz/subsurface-viewer/dist/components/Map"; -import type { Feature, FeatureCollection } from "geojson"; +import { isEqual } from "lodash"; export type WellborePickLayerData = { easting: number; @@ -14,11 +23,6 @@ export type WellborePickLayerData = { slotName: string; }; -type TextLayerData = { - coordinates: [number, number, number]; - name: string; -}; - export interface WellBorePicksLayerProps extends ExtendedLayerProps { id: string; data: WellborePickLayerData[]; @@ -28,10 +32,28 @@ export interface WellBorePicksLayerProps extends ExtendedLayerProps { reportBoundingBox?: React.Dispatch; } +// properties.name is required to trigger tooltip in Map.tsx component in subsurface-viewer +type PointsData = { coordinates: [number, number, number]; properties: { name: string } }; + export class WellborePicksLayer extends CompositeLayer { static layerName: string = "WellborePicksLayer"; - private _textData: TextLayerData[] = []; - private _pointsData: FeatureCollection | null = null; + + // @ts-ignore - This is how deck.gl expects the state to be defined + // For instance, see her: + // https://github.com/visgl/deck.gl/blob/master/modules/layers/src/point-cloud-layer/point-cloud-layer.ts#L123 + state!: { + pointsData: PointsData[]; + hoveredIndex: number | null; + }; + + initializeState(context: LayerContext): void { + super.initializeState(context); + + this.state = { + pointsData: [], + hoveredIndex: null, + }; + } filterSubLayer(context: FilterContext): boolean { if (context.layer.id.includes("text")) { @@ -63,98 +85,72 @@ export class WellborePicksLayer extends CompositeLayer return [minX, minY, minZ, maxX, maxY, maxZ]; } - updateState(params: UpdateParameters>>): void { - const features: Feature[] = params.props.data.map((wellPick) => { + updateState({ + changeFlags, + props, + oldProps, + }: UpdateParameters>>): void { + if (!changeFlags.dataChanged) { + return; + } + + if (isEqual(props.data, oldProps.data)) { + return; + } + + const pointsData: PointsData[] = props.data.map((wellPick) => { return { - type: "Feature", - geometry: { - type: "Point", - coordinates: [wellPick.easting, wellPick.northing, wellPick.tvdMsl], - }, + coordinates: [wellPick.easting, wellPick.northing, wellPick.tvdMsl], properties: { name: `${wellPick.wellBoreUwi}, TVD_MSL: ${wellPick.tvdMsl}, MD: ${wellPick.md}`, - color: [100, 100, 100, 100], }, }; }); - const pointsData: FeatureCollection = { - type: "FeatureCollection", - features: features, - }; - - const textData: TextLayerData[] = this.props.data.map((wellPick) => { - return { - coordinates: [wellPick.easting, wellPick.northing, wellPick.tvdMsl], - name: wellPick.wellBoreUwi, - }; + this.setState({ + pointsData, }); - this._pointsData = pointsData; - this._textData = textData; - this.props.reportBoundingBox?.({ layerBoundingBox: this.calcBoundingBox(), }); } + onHover(info: PickingInfo): boolean { + const { index } = info; + this.setState({ hoveredIndex: index }); + + return false; + } + renderLayers() { - const fontSize = 16; - const sizeMinPixels = 16; - const sizeMaxPixels = 16; + const { zIncreaseDownwards } = this.props; + const { pointsData, hoveredIndex } = this.state; return [ - new GeoJsonLayer( - this.getSubLayerProps({ - id: "points", - data: this._pointsData ?? undefined, - filled: true, - lineWidthMinPixels: 5, - lineWidthMaxPixels: 5, - lineWidthUnits: "meters", - parameters: { - depthTest: false, - }, - getLineWidth: 1, - depthTest: false, - pickable: true, - getText: (d: Feature) => d.properties?.wellBoreUwi, - getLineColor: [50, 50, 50], - }) - ), - - /* - new TextLayer( - this.getSubLayerProps({ - id: "text", - data: this._textData, - pickable: true, - getColor: [255, 255, 255], - fontWeight: 800, - fontSettings: { - fontSize: fontSize * 2, - sdf: true, - }, - outlineColor: [0, 0, 0], - outlineWidth: 2, - getSize: 12, - sdf: true, - sizeScale: fontSize, - sizeUnits: "meters", - sizeMinPixels: sizeMinPixels, - sizeMaxPixels: sizeMaxPixels, - getAlignmentBaseline: "top", - getTextAnchor: "middle", - getPosition: (d: TextLayerData) => d.coordinates, - getText: (d: TextLayerData) => d.name, - extensions: [ - new CollisionFilterExtension({ - collisionEnabled: true, - }), - ], - }) - ), - */ + new PointCloudLayer({ + id: `${this.props.id}-points`, + data: pointsData, + pickable: true, + getPosition: (d) => { + const zFactor = zIncreaseDownwards ? -1 : 1; + return [d.coordinates[0], d.coordinates[1], d.coordinates[2] * zFactor]; + }, + getColor: (_, ctx) => { + if (ctx.index === hoveredIndex) { + return COLORS.hover; + } + + return [100, 100, 100]; + }, + pointSize: 15, + sizeUnits: "meters", + material: { ambient: 0.75, diffuse: 0.4, shininess: 0, specularColor: [0, 0, 0] }, + updateTriggers: { + getColor: [hoveredIndex], + getPosition: [zIncreaseDownwards], + }, + }), ]; } } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts new file mode 100644 index 000000000..0b0157c20 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts @@ -0,0 +1,49 @@ +import { CompositeLayer, LayersList } from "@deck.gl/core"; +import { ExtendedLayerProps } from "@webviz/subsurface-viewer"; +import { BoundingBox3D, ReportBoundingBoxAction } from "@webviz/subsurface-viewer/dist/components/Map"; + +import { PipeLayer } from "./_private/PipeLayer"; + +export type WellsLayerData = { + coordinates: [number, number, number][]; + properties: { uuid: string; name: string }; +}[]; + +export interface WellsLayerProps extends ExtendedLayerProps { + id: string; + data: WellsLayerData; + zIncreaseDownwards?: boolean; + + boundingBox: BoundingBox3D; + + // Non public properties: + reportBoundingBox?: React.Dispatch; +} + +export class WellsLayer extends CompositeLayer { + static layerName: string = "WellsLayer"; + + updateState(params: any): void { + super.updateState(params); + + const { boundingBox } = this.props; + + this.props.reportBoundingBox?.({ + layerBoundingBox: boundingBox, + }); + } + + renderLayers(): LayersList { + return [ + new PipeLayer({ + id: "pipelayer", + data: this.props.data.map((well) => { + return well.coordinates.map((coord) => { + return { x: coord[0], y: coord[1], z: coord[2] }; + }); + }), + material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, + }), + ]; + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Line.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Line.ts new file mode 100644 index 000000000..3231cf035 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Line.ts @@ -0,0 +1,32 @@ +import * as vec3 from "@lib/utils/vec3"; + +export type Line = { + point: vec3.Vec3; + direction: vec3.Vec3; +}; + +export function fromPoints(p1: vec3.Vec3, p2: vec3.Vec3): Line { + return { + point: p1, + direction: vec3.subtract(p2, p1), + }; +} + +export function fromPointAndDirection(point: vec3.Vec3, direction: vec3.Vec3): Line { + return { + point, + direction, + }; +} + +export function intersect(line1: Line, line2: Line): vec3.Vec3 | null { + const cross = vec3.cross(line1.direction, line2.direction); + const crossLength = vec3.length(cross); + if (crossLength < 1e-6) { + return null; + } + + const line1ToLine2 = vec3.subtract(line2.point, line1.point); + const t = vec3.dot(vec3.cross(line1ToLine2, line2.direction), cross) / crossLength ** 2; + return vec3.add(line1.point, vec3.scale(line1.direction, t)); +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Mat3.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Mat3.ts new file mode 100644 index 000000000..e0310d58c --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Mat3.ts @@ -0,0 +1,113 @@ +/** + * A 3x3 matrix. + * + * The matrix is stored in column-major order. + */ +export type Mat3 = { + m00: number; + m01: number; + m02: number; + m10: number; + m11: number; + m12: number; + m20: number; + m21: number; + m22: number; +}; + +export function setRow(matrix: Mat3, rowIndex: number, x: number, y: number, z: number): Mat3 { + switch (rowIndex) { + case 0: + return { + ...matrix, + m00: x, + m01: y, + m02: z, + }; + case 1: + return { + ...matrix, + m10: x, + m11: y, + m12: z, + }; + case 2: + return { + ...matrix, + m20: x, + m21: y, + m22: z, + }; + default: + throw new Error(`Invalid row index: ${rowIndex}`); + } +} + +export function setColumn(matrix: Mat3, columnIndex: number, x: number, y: number, z: number): Mat3 { + switch (columnIndex) { + case 0: + return { + ...matrix, + m00: x, + m10: y, + m20: z, + }; + case 1: + return { + ...matrix, + m01: x, + m11: y, + m21: z, + }; + case 2: + return { + ...matrix, + m02: x, + m12: y, + m22: z, + }; + default: + throw new Error(`Invalid column index: ${columnIndex}`); + } +} + +export function transpose(matrix: Mat3) { + matrix.m01 = matrix.m10; + matrix.m02 = matrix.m20; + matrix.m10 = matrix.m01; + matrix.m12 = matrix.m21; + matrix.m20 = matrix.m02; + matrix.m21 = matrix.m12; +} + +export function invert(matrix: Mat3) { + const tmp: number[] = []; + + tmp[0] = matrix.m11 * matrix.m22 - matrix.m12 * matrix.m21; + tmp[1] = matrix.m21 * matrix.m02 - matrix.m22 * matrix.m01; + tmp[2] = matrix.m01 * matrix.m12 - matrix.m02 * matrix.m11; + tmp[3] = matrix.m12 * matrix.m20 - matrix.m10 * matrix.m22; + tmp[4] = matrix.m22 * matrix.m00 - matrix.m20 * matrix.m02; + tmp[5] = matrix.m02 * matrix.m10 - matrix.m00 * matrix.m12; + tmp[6] = matrix.m10 * matrix.m21 - matrix.m11 * matrix.m20; + tmp[7] = matrix.m20 * matrix.m01 - matrix.m21 * matrix.m00; + tmp[8] = matrix.m00 * matrix.m11 - matrix.m01 * matrix.m10; + + const det = matrix.m00 * tmp[0] + matrix.m01 * tmp[3] + matrix.m02 * tmp[6]; + + if (det === 0) { + throw new Error("Matrix is not invertible"); + } + + const invDet = 1.0 / det; + + matrix.m00 = tmp[0] * invDet; + matrix.m01 = tmp[1] * invDet; + matrix.m02 = tmp[2] * invDet; + matrix.m10 = tmp[3] * invDet; + matrix.m11 = tmp[4] * invDet; + matrix.m12 = tmp[5] * invDet; + matrix.m20 = tmp[6] * invDet; + matrix.m21 = tmp[7] * invDet; + matrix.m22 = tmp[8] * invDet; +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Mat4.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Mat4.ts new file mode 100644 index 000000000..6fa08f76e --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Mat4.ts @@ -0,0 +1,465 @@ +import * as vec3 from "@lib/utils/vec3"; + +import * as mat3 from "./Mat3"; + +const EPSILON = 0.00001; + +/** + * A 4x4 matrix. + * + * The matrix is stored in column-major order. + * + */ +export type Mat4 = { + m00: number; + m01: number; + m02: number; + m03: number; + m10: number; + m11: number; + m12: number; + m13: number; + m20: number; + m21: number; + m22: number; + m23: number; + m30: number; + m31: number; + m32: number; + m33: number; +}; + +export function identity(): Mat4 { + return { + m00: 1, + m01: 0, + m02: 0, + m03: 0, + m10: 0, + m11: 1, + m12: 0, + m13: 0, + m20: 0, + m21: 0, + m22: 1, + m23: 0, + m30: 0, + m31: 0, + m32: 0, + m33: 1, + }; +} + +export function setRow(matrix: Mat4, rowIndex: number, x: number, y: number, z: number, w: number): Mat4 { + switch (rowIndex) { + case 0: + return { + ...matrix, + m00: x, + m01: y, + m02: z, + m03: w, + }; + case 1: + return { + ...matrix, + m10: x, + m11: y, + m12: z, + m13: w, + }; + case 2: + return { + ...matrix, + m20: x, + m21: y, + m22: z, + m23: w, + }; + case 3: + return { + ...matrix, + m30: x, + m31: y, + m32: z, + m33: w, + }; + default: + return matrix; + } +} + +export function setColumn(matrix: Mat4, columnIndex: number, x: number, y: number, z: number, w: number): Mat4 { + switch (columnIndex) { + case 0: + return { + ...matrix, + m00: x, + m10: y, + m20: z, + m30: w, + }; + case 1: + return { + ...matrix, + m01: x, + m11: y, + m21: z, + m31: w, + }; + case 2: + return { + ...matrix, + m02: x, + m12: y, + m22: z, + m32: w, + }; + case 3: + return { + ...matrix, + m03: x, + m13: y, + m23: z, + m33: w, + }; + default: + return matrix; + } +} + +export function transpose(matrix: Mat4) { + matrix.m01 = matrix.m10; + matrix.m02 = matrix.m20; + matrix.m03 = matrix.m30; + matrix.m10 = matrix.m01; + matrix.m12 = matrix.m21; + matrix.m13 = matrix.m31; + matrix.m20 = matrix.m02; + matrix.m21 = matrix.m12; + matrix.m23 = matrix.m32; + matrix.m30 = matrix.m03; + matrix.m31 = matrix.m13; + matrix.m32 = matrix.m23; +} + +export function invert(matrix: Mat4) { + if (matrix.m03 == 0 && matrix.m13 == 0 && matrix.m23 == 0 && matrix.m33 == 1) { + invertAffine(matrix); + return; + } +} + +export function invertAffine(matrix: Mat4) { + const r: mat3.Mat3 = { + m00: matrix.m00, + m01: matrix.m01, + m02: matrix.m02, + m10: matrix.m10, + m11: matrix.m11, + m12: matrix.m12, + m20: matrix.m20, + m21: matrix.m21, + m22: matrix.m22, + }; + + mat3.invert(r); + + matrix.m00 = r.m00; + matrix.m01 = r.m01; + matrix.m02 = r.m02; + matrix.m10 = r.m10; + matrix.m11 = r.m11; + matrix.m12 = r.m12; + matrix.m20 = r.m20; + matrix.m21 = r.m21; + matrix.m22 = r.m22; + + const x = matrix.m30; + const y = matrix.m31; + const z = matrix.m32; + + matrix.m30 = -(r.m00 * x + r.m10 * y + r.m20 * z); + matrix.m31 = -(r.m01 * x + r.m11 * y + r.m21 * z); + matrix.m32 = -(r.m02 * x + r.m12 * y + r.m22 * z); +} + +export function invertGeneral(matrix: Mat4) { + const cofactor0 = getCofactor( + matrix.m11, + matrix.m12, + matrix.m13, + matrix.m21, + matrix.m22, + matrix.m23, + matrix.m31, + matrix.m32, + matrix.m33 + ); + const cofactor1 = getCofactor( + matrix.m10, + matrix.m12, + matrix.m13, + matrix.m20, + matrix.m22, + matrix.m23, + matrix.m30, + matrix.m32, + matrix.m33 + ); + const cofactor2 = getCofactor( + matrix.m10, + matrix.m11, + matrix.m13, + matrix.m20, + matrix.m21, + matrix.m23, + matrix.m30, + matrix.m31, + matrix.m33 + ); + const cofactor3 = getCofactor( + matrix.m10, + matrix.m11, + matrix.m12, + matrix.m20, + matrix.m21, + matrix.m22, + matrix.m30, + matrix.m31, + matrix.m32 + ); + + const determinant = + matrix.m00 * cofactor0 - matrix.m01 * cofactor1 + matrix.m02 * cofactor2 - matrix.m03 * cofactor3; + if (Math.abs(determinant) <= EPSILON) { + matrix = identity(); + } + + const cofactor4 = getCofactor( + matrix.m01, + matrix.m02, + matrix.m03, + matrix.m21, + matrix.m22, + matrix.m23, + matrix.m31, + matrix.m32, + matrix.m33 + ); + const cofactor5 = getCofactor( + matrix.m00, + matrix.m02, + matrix.m03, + matrix.m20, + matrix.m22, + matrix.m23, + matrix.m30, + matrix.m32, + matrix.m33 + ); + const cofactor6 = getCofactor( + matrix.m00, + matrix.m01, + matrix.m03, + matrix.m20, + matrix.m21, + matrix.m23, + matrix.m30, + matrix.m31, + matrix.m33 + ); + const cofactor7 = getCofactor( + matrix.m00, + matrix.m01, + matrix.m02, + matrix.m20, + matrix.m21, + matrix.m22, + matrix.m30, + matrix.m31, + matrix.m32 + ); + + const cofactor8 = getCofactor( + matrix.m01, + matrix.m02, + matrix.m03, + matrix.m11, + matrix.m12, + matrix.m13, + matrix.m31, + matrix.m32, + matrix.m33 + ); + const cofactor9 = getCofactor( + matrix.m00, + matrix.m02, + matrix.m03, + matrix.m10, + matrix.m12, + matrix.m13, + matrix.m30, + matrix.m32, + matrix.m33 + ); + const cofactor10 = getCofactor( + matrix.m00, + matrix.m01, + matrix.m03, + matrix.m10, + matrix.m11, + matrix.m13, + matrix.m30, + matrix.m31, + matrix.m33 + ); + const cofactor11 = getCofactor( + matrix.m00, + matrix.m01, + matrix.m02, + matrix.m10, + matrix.m11, + matrix.m12, + matrix.m30, + matrix.m31, + matrix.m32 + ); + + const cofactor12 = getCofactor( + matrix.m01, + matrix.m02, + matrix.m03, + matrix.m11, + matrix.m12, + matrix.m13, + matrix.m21, + matrix.m22, + matrix.m23 + ); + const cofactor13 = getCofactor( + matrix.m00, + matrix.m02, + matrix.m03, + matrix.m10, + matrix.m12, + matrix.m13, + matrix.m20, + matrix.m22, + matrix.m23 + ); + const cofactor14 = getCofactor( + matrix.m00, + matrix.m01, + matrix.m03, + matrix.m10, + matrix.m11, + matrix.m13, + matrix.m20, + matrix.m21, + matrix.m23 + ); + const cofactor15 = getCofactor( + matrix.m00, + matrix.m01, + matrix.m02, + matrix.m10, + matrix.m11, + matrix.m12, + matrix.m20, + matrix.m21, + matrix.m22 + ); + + const invDeterminant = 1 / determinant; + + matrix.m00 = cofactor0 * invDeterminant; + matrix.m01 = -cofactor4 * invDeterminant; + matrix.m02 = cofactor8 * invDeterminant; + matrix.m03 = -cofactor12 * invDeterminant; + + matrix.m10 = -cofactor1 * invDeterminant; + matrix.m11 = cofactor5 * invDeterminant; + matrix.m12 = -cofactor9 * invDeterminant; + matrix.m13 = cofactor13 * invDeterminant; + + matrix.m20 = cofactor2 * invDeterminant; + matrix.m21 = -cofactor6 * invDeterminant; + matrix.m22 = cofactor10 * invDeterminant; + matrix.m23 = -cofactor14 * invDeterminant; + + matrix.m30 = -cofactor3 * invDeterminant; + matrix.m31 = cofactor7 * invDeterminant; + matrix.m32 = -cofactor11 * invDeterminant; + matrix.m33 = cofactor15 * invDeterminant; +} + +function getCofactor( + m0: number, + m1: number, + m2: number, + m3: number, + m4: number, + m5: number, + m6: number, + m7: number, + m8: number +): number { + return m0 * (m4 * m8 - m5 * m7) - m1 * (m3 * m8 - m5 * m6) + m2 * (m3 * m7 - m4 * m6); +} + +export function lookAt(matrix: Mat4, target: vec3.Vec3) { + const position = { x: matrix.m30, y: matrix.m31, z: matrix.m32 }; + const forward = vec3.normalize(vec3.subtract(target, position)); + let up: vec3.Vec3 = { x: 0, y: 0, z: 1 }; + let left: vec3.Vec3 = { x: 0, y: 1, z: 0 }; + + if (Math.abs(forward.x) < EPSILON && Math.abs(forward.z) < EPSILON) { + if (forward.y > 0) { + up = { x: 0, y: 0, z: -1 }; + } + } else { + up = { x: 0, y: 1, z: 0 }; + } + + left = vec3.normalize(vec3.cross(up, forward)); + up = vec3.cross(forward, left); + + matrix.m00 = left.x; + matrix.m01 = left.y; + matrix.m02 = left.z; + + matrix.m10 = up.x; + matrix.m11 = up.y; + matrix.m12 = up.z; + + matrix.m20 = forward.x; + matrix.m21 = forward.y; + matrix.m22 = forward.z; +} + +export function translate(matrix: Mat4, v: vec3.Vec3) { + matrix.m00 += matrix.m03 * v.x; + matrix.m01 += matrix.m03 * v.y; + matrix.m02 += matrix.m03 * v.z; + + matrix.m10 += matrix.m13 * v.x; + matrix.m11 += matrix.m13 * v.y; + matrix.m12 += matrix.m13 * v.z; + + matrix.m20 += matrix.m23 * v.x; + matrix.m21 += matrix.m23 * v.y; + matrix.m22 += matrix.m23 * v.z; + + matrix.m30 += matrix.m33 * v.x; + matrix.m31 += matrix.m33 * v.y; + matrix.m32 += matrix.m33 * v.z; +} + +export function multiply(matrix: Mat4, vector: vec3.Vec3): vec3.Vec3 { + return { + x: matrix.m00 * vector.x + matrix.m10 * vector.y + matrix.m20 * vector.z + matrix.m30, + y: matrix.m01 * vector.x + matrix.m11 * vector.y + matrix.m21 * vector.z + matrix.m31, + z: matrix.m02 * vector.x + matrix.m12 * vector.y + matrix.m22 * vector.z + matrix.m32, + }; +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Pipe.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Pipe.ts new file mode 100644 index 000000000..6d5474e91 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Pipe.ts @@ -0,0 +1,105 @@ +import * as vec3 from "@lib/utils/vec3"; + +import { cloneDeep } from "lodash"; + +import * as mat4 from "./Mat4"; +import * as plane from "./Plane"; + +export class Pipe { + private _path: vec3.Vec3[] = []; + private _contour: vec3.Vec3[] = []; + private _contours: vec3.Vec3[][] = []; + private _normals: vec3.Vec3[][] = []; + + constructor(pathPoints: vec3.Vec3[], contourPoints: vec3.Vec3[]) { + this._path = pathPoints; + this._contour = cloneDeep(contourPoints); + + this.generateContours(); + } + + getPath(): vec3.Vec3[] { + return this._path; + } + + getContours(): vec3.Vec3[][] { + return this._contours; + } + + getNormals(): vec3.Vec3[][] { + return this._normals; + } + + getContourCount(): number { + return this._contours.length; + } + + private generateContours() { + this._contours = []; + this._normals = []; + + if (this._path.length < 1) { + return; + } + + this.transformFirstContour(); + + this._contours.push(this._contour); + this._normals.push(this.computeContourNormal(0)); + + for (let i = 1; i < this._path.length; ++i) { + this._contours.push(this.projectContour(i - 1, i)); + this._normals.push(this.computeContourNormal(i)); + } + } + + private projectContour(fromIndex: number, toIndex: number): vec3.Vec3[] { + const dir1 = vec3.subtract(this._path[toIndex], this._path[fromIndex]); + + let dir2 = dir1; + if (toIndex < this._path.length - 1) { + dir2 = vec3.subtract(this._path[toIndex + 1], this._path[toIndex]); + } + + const normal = vec3.add(dir1, dir2); + const pl = plane.fromNormalAndPoint(normal, this._path[toIndex]); + + const fromContour = this._contours[fromIndex]; + let toContour: vec3.Vec3[] = []; + + for (let i = 0; i < fromContour.length; ++i) { + toContour.push(plane.intersectLine(pl, { point: fromContour[i], direction: dir1 })!); + } + + return toContour; + } + + private transformFirstContour() { + const matrix: mat4.Mat4 = mat4.identity(); + + if (this._path.length > 0) { + if (this._path.length > 1) { + mat4.lookAt(matrix, vec3.subtract(this._path[1], this._path[0])); + } + + mat4.translate(matrix, this._path[0]); + + for (let i = 0; i < this._contour.length; ++i) { + this._contour[i] = mat4.multiply(matrix, this._contour[i]); + } + } + } + + private computeContourNormal(pathIndex: number): vec3.Vec3[] { + const contour = this._contours[pathIndex]; + const center = this._path[pathIndex]; + + const contourNormal: vec3.Vec3[] = []; + for (let i = 0; i < contour.length; ++i) { + const normal = vec3.normalize(vec3.subtract(contour[i], center)); + contourNormal.push(normal); + } + + return contourNormal; + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts new file mode 100644 index 000000000..5ce096ae1 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts @@ -0,0 +1,171 @@ +import { Layer, LayerContext, Material, UpdateParameters, picking, project32 } from "@deck.gl/core"; +import { Vec3 } from "@lib/utils/vec3"; +import { Geometry, Model } from "@luma.gl/engine"; +import { ParsedPBRMaterial } from "@luma.gl/gltf"; +import { phongLighting } from "@luma.gl/shadertools"; + +import { Pipe } from "./Pipe"; +import fragmentShader from "./shaders/fragmentShader.glsl?raw"; +import vertexShader from "./shaders/vertexShader.glsl?raw"; + +export type PipeLayerProps = { + id: string; + data: Vec3[][]; + material?: Material; +}; + +export class PipeLayer extends Layer { + static layerName: string = "PipeLayer"; + + // @ts-ignore - This is how deck.gl expects the state to be defined + state!: { + parsedPBRMaterial?: ParsedPBRMaterial; + models: Model[]; + }; + + initializeState(context: LayerContext): void { + this.state = { + models: this.makeModels(context), + }; + } + + draw(): void { + const { models } = this.state; + for (const model of models) { + model.draw(this.context.renderPass); + } + } + + updateState(params: UpdateParameters>): void { + if (params.changeFlags.dataChanged) { + this.setState({ + models: this.makeModels(params.context), + }); + } + } + + makeModels(context: LayerContext): Model[] { + const pipes = this.makePipes(); + const meshes = this.makeMeshes(pipes); + + const models: Model[] = []; + for (const [idx, mesh] of meshes.entries()) { + models.push( + new Model(context.device, { + id: `${this.id}-mesh-${idx}`, + geometry: mesh, + modules: [project32, phongLighting, picking], + vs: vertexShader, + fs: fragmentShader, + }) + ); + } + + return models; + } + + private makePipes(): Pipe[] { + const { data } = this.props; + const circle = this.makeCircle(10, 16); + + const pipes: Pipe[] = []; + for (const points of data) { + pipes.push(new Pipe(points, circle)); + } + + return pipes; + } + + private makeCircle(radius: number, segments: number): Vec3[] { + const points: Vec3[] = []; + if (segments < 2) { + return points; + } + + const pi2 = Math.PI * 2; + for (let i = 0; i < segments; i++) { + const angle = (i / segments) * pi2; + points.push({ x: Math.cos(angle) * radius, y: Math.sin(angle) * radius, z: 0 }); + } + + return points; + } + + private makeMeshes(pipes: Pipe[]): Geometry[] { + const geometries: Geometry[] = []; + for (const pipe of pipes) { + const numContours = pipe.getContourCount(); + const numVerticesPerContour = pipe.getContours()[0].length; + + const vertices = new Float32Array(numContours * numVerticesPerContour * 3 + 2 * 3); + const indices = new Uint32Array( + (numContours - 1) * numVerticesPerContour * 6 + numVerticesPerContour * 2 * 3 + ); + let verticesIndex: number = 0; + let indicesIndex: number = 0; + + for (let i = 0; i < numContours; i++) { + if (i === 0) { + const vertex = pipe.getPath()[0]; + vertices[verticesIndex++] = vertex.x; + vertices[verticesIndex++] = vertex.y; + vertices[verticesIndex++] = vertex.z; + } + const contour = pipe.getContours()[i]; + for (let j = 0; j < numVerticesPerContour; j++) { + const vertex = contour[j]; + vertices[verticesIndex++] = vertex.x; + vertices[verticesIndex++] = vertex.y; + vertices[verticesIndex++] = vertex.z; + + if (i === 0) { + indices[indicesIndex++] = j + 2 > numVerticesPerContour ? 1 : j + 2; + indices[indicesIndex++] = j + 1; + indices[indicesIndex++] = 0; + } else { + if (j === numVerticesPerContour - 1) { + indices[indicesIndex++] = i * numVerticesPerContour + j + 1; + indices[indicesIndex++] = i * numVerticesPerContour + 1; + indices[indicesIndex++] = i * numVerticesPerContour; + indices[indicesIndex++] = i * numVerticesPerContour; + indices[indicesIndex++] = i * numVerticesPerContour + 1; + indices[indicesIndex++] = i * numVerticesPerContour + 1 - numVerticesPerContour; + } else { + const index = i * numVerticesPerContour + j + 1; + indices[indicesIndex++] = index - numVerticesPerContour; + indices[indicesIndex++] = index; + indices[indicesIndex++] = index - numVerticesPerContour + 1; + indices[indicesIndex++] = index - numVerticesPerContour + 1; + indices[indicesIndex++] = index; + indices[indicesIndex++] = index + 1; + } + } + } + } + + const vertex = pipe.getPath()[pipe.getPath().length - 1]; + vertices[verticesIndex++] = vertex.x; + vertices[verticesIndex++] = vertex.y; + vertices[verticesIndex++] = vertex.z; + + for (let j = 0; j < numVerticesPerContour; j++) { + const index = (numContours - 1) * numVerticesPerContour + 1; + indices[indicesIndex++] = verticesIndex / 3 - 1; + indices[indicesIndex++] = index + j; + indices[indicesIndex++] = j + 1 >= numVerticesPerContour ? index : index + j + 1; + } + + geometries.push( + new Geometry({ + topology: "triangle-list", + attributes: { + positions: vertices, + }, + indices: indices, + }) + ); + } + + return geometries; + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Plane.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Plane.ts new file mode 100644 index 000000000..5a209b27c --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/Plane.ts @@ -0,0 +1,64 @@ +import * as vec3 from "@lib/utils/vec3"; + +import { Line } from "./Line"; + +export type Plane = { + normal: vec3.Vec3; + point: vec3.Vec3; + d: number; // Coefficient of constant term: d = -(a*x0 + b*y0 + c*z0) + normalLength: number; + distanceToOrigin: number; +}; + +export function fromNormalAndPoint(normal: vec3.Vec3, point: vec3.Vec3): Plane { + const d = -vec3.dot(normal, point); + const normalLength = vec3.length(normal); + const distanceToOrigin = -d / normalLength; + return { normal, point, d, normalLength, distanceToOrigin }; +} + +export function getDistance(plane: Plane, point: vec3.Vec3): number { + const dot = vec3.dot(plane.normal, point); + return (dot + plane.d) / plane.normalLength; +} + +export function normalize(plane: Plane): Plane { + const inversedLength = 1.0 / plane.normalLength; + const normal = vec3.normalize(plane.normal); + const d = plane.d * inversedLength; + return { + normal, + point: plane.point, + d, + normalLength: 1.0, + distanceToOrigin: plane.distanceToOrigin * inversedLength, + }; +} + +export function intersectLine(plane: Plane, line: Line): vec3.Vec3 | null { + const dot1 = vec3.dot(plane.normal, line.point); + const dot2 = vec3.dot(plane.normal, line.direction); + + if (dot2 === 0) { + return null; + } + + const t = -(dot1 + plane.d) / dot2; + + return vec3.add(line.point, vec3.scale(line.direction, t)); +} + +export function intersectPlane(plane1: Plane, plane2: Plane): Line | null { + const v = vec3.cross(plane1.normal, plane2.normal); + + if (v.x === 0 && v.y === 0 && v.z === 0) { + return null; + } + + const dot = vec3.dot(v, v); + const n1 = vec3.scale(plane2.normal, plane1.d); + const n2 = vec3.scale(plane1.normal, -plane2.d); + const p = vec3.scale(vec3.cross(vec3.add(n1, n2), v), 1 / dot); + + return { point: p, direction: v }; +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl new file mode 100644 index 000000000..7bd17ca1a --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl @@ -0,0 +1,19 @@ +#version 300 es +#define SHADER_NAME wells-layer-fragment-shader + +precision highp float; + +in vec3 cameraPosition; +in vec4 position_commonspace; +flat in vec3 normal; + +out vec4 fragColor; + +void main(void) { + vec4 color = vec4(0.85f, 0.85f, 0.85f, 1.0f); + fragColor = color; + + DECKGL_FILTER_COLOR(color, geometry); + vec3 lightColor = lighting_getLightColor(color.rgb, cameraPosition, position_commonspace.xyz, -normal); + fragColor = vec4(lightColor, color.a); +} \ No newline at end of file diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl new file mode 100644 index 000000000..63ba5d483 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl @@ -0,0 +1,27 @@ +#version 300 es +#define SHADER_NAME wells-layer-vertex-shader + +in vec3 positions; +in vec3 normals; + +flat out vec3 normal; +out vec3 cameraPosition; +out vec4 position_commonspace; + +void main(void) +{ + cameraPosition = project_uCameraPosition; + + normal = normals; + + position_commonspace = vec4(project_position(positions), 0.0); + + geometry.position = position_commonspace; + + gl_Position = project_common_position_to_clipspace(position_commonspace); + + DECKGL_FILTER_GL_POSITION(gl_Position, geometry); + + vec4 color = vec4(0.0); + DECKGL_FILTER_COLOR(color, geometry); +} \ No newline at end of file From 38d70fa3642e3a0f40d64c6f2b94fc08d9d69aef Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 25 Feb 2025 02:54:57 +0100 Subject: [PATCH 61/97] wip --- .../WellsLayer/WellsLayer.ts | 2 +- .../WellsLayer/_private/PipeLayer.ts | 99 ++++++++++++++----- .../_private/shaders/fragmentShader.glsl | 5 +- 3 files changed, 77 insertions(+), 29 deletions(-) diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts index 0b0157c20..85645c609 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts @@ -42,7 +42,7 @@ export class WellsLayer extends CompositeLayer { return { x: coord[0], y: coord[1], z: coord[2] }; }); }), - material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, + material: true, }), ]; } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts index 5ce096ae1..69bee59a0 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts @@ -1,7 +1,7 @@ import { Layer, LayerContext, Material, UpdateParameters, picking, project32 } from "@deck.gl/core"; import { Vec3 } from "@lib/utils/vec3"; +import * as vec3 from "@lib/utils/vec3"; import { Geometry, Model } from "@luma.gl/engine"; -import { ParsedPBRMaterial } from "@luma.gl/gltf"; import { phongLighting } from "@luma.gl/shadertools"; import { Pipe } from "./Pipe"; @@ -19,7 +19,6 @@ export class PipeLayer extends Layer { // @ts-ignore - This is how deck.gl expects the state to be defined state!: { - parsedPBRMaterial?: ParsedPBRMaterial; models: Model[]; }; @@ -97,20 +96,42 @@ export class PipeLayer extends Layer { const numContours = pipe.getContourCount(); const numVerticesPerContour = pipe.getContours()[0].length; - const vertices = new Float32Array(numContours * numVerticesPerContour * 3 + 2 * 3); + const vertices = new Float32Array((numContours + 2) * numVerticesPerContour * 3 + 2 * 3); const indices = new Uint32Array( (numContours - 1) * numVerticesPerContour * 6 + numVerticesPerContour * 2 * 3 ); + const normals = new Float32Array((numContours + 2) * numVerticesPerContour * 3 + 2 * 3); + let verticesIndex: number = 0; let indicesIndex: number = 0; + let normalsIndex: number = 0; + + const startVertex = pipe.getPath()[0]; + vertices[verticesIndex++] = startVertex.x; + vertices[verticesIndex++] = startVertex.y; + vertices[verticesIndex++] = startVertex.z; + + const normal = vec3.normalize(vec3.cross(pipe.getNormals()[0][1], pipe.getNormals()[0][0])); + normals[normalsIndex++] = normal.x; + normals[normalsIndex++] = normal.y; + normals[normalsIndex++] = normal.z; + + for (let j = 0; j < numVerticesPerContour; j++) { + const vertex = pipe.getContours()[0][j]; + vertices[verticesIndex++] = vertex.x; + vertices[verticesIndex++] = vertex.y; + vertices[verticesIndex++] = vertex.z; + + normals[normalsIndex++] = normal.x; + normals[normalsIndex++] = normal.y; + normals[normalsIndex++] = normal.z; + + indices[indicesIndex++] = 0; + indices[indicesIndex++] = j + 1; + indices[indicesIndex++] = j + 2 > numVerticesPerContour ? 1 : j + 2; + } for (let i = 0; i < numContours; i++) { - if (i === 0) { - const vertex = pipe.getPath()[0]; - vertices[verticesIndex++] = vertex.x; - vertices[verticesIndex++] = vertex.y; - vertices[verticesIndex++] = vertex.z; - } const contour = pipe.getContours()[i]; for (let j = 0; j < numVerticesPerContour; j++) { const vertex = contour[j]; @@ -118,20 +139,21 @@ export class PipeLayer extends Layer { vertices[verticesIndex++] = vertex.y; vertices[verticesIndex++] = vertex.z; - if (i === 0) { - indices[indicesIndex++] = j + 2 > numVerticesPerContour ? 1 : j + 2; - indices[indicesIndex++] = j + 1; - indices[indicesIndex++] = 0; - } else { + const normal = pipe.getNormals()[i][j]; + normals[normalsIndex++] = normal.x; + normals[normalsIndex++] = normal.y; + normals[normalsIndex++] = normal.z; + + if (i > 0) { + const index = 1 + (i + 1) * numVerticesPerContour + j; if (j === numVerticesPerContour - 1) { - indices[indicesIndex++] = i * numVerticesPerContour + j + 1; - indices[indicesIndex++] = i * numVerticesPerContour + 1; - indices[indicesIndex++] = i * numVerticesPerContour; - indices[indicesIndex++] = i * numVerticesPerContour; - indices[indicesIndex++] = i * numVerticesPerContour + 1; - indices[indicesIndex++] = i * numVerticesPerContour + 1 - numVerticesPerContour; + indices[indicesIndex++] = index - numVerticesPerContour; + indices[indicesIndex++] = index; + indices[indicesIndex++] = index - 2 * numVerticesPerContour + 1; + indices[indicesIndex++] = index - 2 * numVerticesPerContour + 1; + indices[indicesIndex++] = index; + indices[indicesIndex++] = (i + 1) * numVerticesPerContour + 1; } else { - const index = i * numVerticesPerContour + j + 1; indices[indicesIndex++] = index - numVerticesPerContour; indices[indicesIndex++] = index; indices[indicesIndex++] = index - numVerticesPerContour + 1; @@ -143,13 +165,34 @@ export class PipeLayer extends Layer { } } - const vertex = pipe.getPath()[pipe.getPath().length - 1]; - vertices[verticesIndex++] = vertex.x; - vertices[verticesIndex++] = vertex.y; - vertices[verticesIndex++] = vertex.z; + const endNormal = vec3.normalize( + vec3.cross( + pipe.getNormals()[pipe.getNormals().length - 1][0], + pipe.getNormals()[pipe.getNormals().length - 1][1] + ) + ); + for (let j = 0; j < numVerticesPerContour; j++) { + const vertex = pipe.getContours()[numContours - 1][j]; + vertices[verticesIndex++] = vertex.x; + vertices[verticesIndex++] = vertex.y; + vertices[verticesIndex++] = vertex.z; + + normals[normalsIndex++] = endNormal.x; + normals[normalsIndex++] = endNormal.y; + normals[normalsIndex++] = endNormal.z; + } + + const endVertex = pipe.getPath()[pipe.getPath().length - 1]; + vertices[verticesIndex++] = endVertex.x; + vertices[verticesIndex++] = endVertex.y; + vertices[verticesIndex++] = endVertex.z; + + normals[normalsIndex++] = endNormal.x; + normals[normalsIndex++] = endNormal.y; + normals[normalsIndex++] = endNormal.z; for (let j = 0; j < numVerticesPerContour; j++) { - const index = (numContours - 1) * numVerticesPerContour + 1; + const index = numContours * numVerticesPerContour + 1; indices[indicesIndex++] = verticesIndex / 3 - 1; indices[indicesIndex++] = index + j; indices[indicesIndex++] = j + 1 >= numVerticesPerContour ? index : index + j + 1; @@ -160,6 +203,10 @@ export class PipeLayer extends Layer { topology: "triangle-list", attributes: { positions: vertices, + normals: { + size: 3, + value: normals, + }, }, indices: indices, }) diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl index 7bd17ca1a..d69e686d2 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl @@ -11,9 +11,10 @@ out vec4 fragColor; void main(void) { vec4 color = vec4(0.85f, 0.85f, 0.85f, 1.0f); - fragColor = color; + + geometry.uv = vec2(0.); DECKGL_FILTER_COLOR(color, geometry); - vec3 lightColor = lighting_getLightColor(color.rgb, cameraPosition, position_commonspace.xyz, -normal); + vec3 lightColor = lighting_getLightColor(color.rgb, cameraPosition, position_commonspace.xyz, normal); fragColor = vec4(lightColor, color.a); } \ No newline at end of file From 31077bf20d7fb6779795b180fe507a4a973f8b0e Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 25 Feb 2025 16:31:14 +0100 Subject: [PATCH 62/97] wip --- .../view/components/ReadoutBoxWrapper.tsx | 11 +- .../visualization/deckgl/makeWellsLayer.ts | 6 +- .../SeismicFenceMeshLayer.ts | 5 +- .../_private/ExtendedSimpleMeshLayer.ts | 1 - .../customDeckGlLayers/WellborePicksLayer.ts | 2 +- .../WellsLayer/WellsLayer.ts | 100 +++++++++++++++-- .../WellsLayer/_private/PipeLayer.ts | 102 ++++++++++++++---- .../_private/shaders/fragmentShader.glsl | 42 +++++++- .../WellsLayer/_private/shaders/uniforms.ts | 23 ++++ .../_private/shaders/vertexShader.glsl | 15 ++- .../_private/wellTrajectoryUtils.ts | 76 +++++++++++++ 11 files changed, 343 insertions(+), 40 deletions(-) create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/uniforms.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/wellTrajectoryUtils.ts diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutBoxWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutBoxWrapper.tsx index 70a488c0b..86734e4bf 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutBoxWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutBoxWrapper.tsx @@ -12,7 +12,7 @@ function makePositionReadout(layerPickInfo: LayerPickInfo): ReadoutItem | null { if (layerPickInfo.coordinate === undefined || layerPickInfo.coordinate.length < 2) { return null; } - return { + const readout = { label: "Position", info: [ { @@ -27,6 +27,15 @@ function makePositionReadout(layerPickInfo: LayerPickInfo): ReadoutItem | null { }, ], }; + if (layerPickInfo.coordinate.length > 2) { + readout.info.push({ + name: "z", + value: layerPickInfo.coordinate[2], + unit: "m", + }); + } + + return readout; } export type ReadoutBoxWrapperProps = { diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts index 188b92293..dfaf95944 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts @@ -103,7 +103,11 @@ export function makeWellsLayer({ number, number ][], - properties: { uuid: wellTrajectory.wellboreUuid, name: wellTrajectory.uniqueWellboreIdentifier }, + properties: { + uuid: wellTrajectory.wellboreUuid, + name: wellTrajectory.uniqueWellboreIdentifier, + mdArray: wellTrajectory.mdArr, + }, }; }); diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts index 5d00997fa..d04144d56 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts @@ -57,7 +57,8 @@ export class SeismicFenceMeshLayer extends CompositeLayer= (256 * 256) - 1) { r = floor(float(vertexIndex) / (256.0 * 256.0)); diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts index c732ad2f5..f699be52b 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts @@ -38,7 +38,7 @@ type PointsData = { coordinates: [number, number, number]; properties: { name: s export class WellborePicksLayer extends CompositeLayer { static layerName: string = "WellborePicksLayer"; - // @ts-ignore - This is how deck.gl expects the state to be defined + // @ts-expect-error - This is how deck.gl expects the state to be defined // For instance, see her: // https://github.com/visgl/deck.gl/blob/master/modules/layers/src/point-cloud-layer/point-cloud-layer.ts#L123 state!: { diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts index 85645c609..2fc48371c 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts @@ -1,12 +1,15 @@ -import { CompositeLayer, LayersList } from "@deck.gl/core"; -import { ExtendedLayerProps } from "@webviz/subsurface-viewer"; +import { CompositeLayer, GetPickingInfoParams, LayersList, PickingInfo } from "@deck.gl/core"; +import { PathLayer, PointCloudLayer } from "@deck.gl/layers"; +import * as vec3 from "@lib/utils/vec3"; +import { ExtendedLayerProps, LayerPickInfo } from "@webviz/subsurface-viewer"; import { BoundingBox3D, ReportBoundingBoxAction } from "@webviz/subsurface-viewer/dist/components/Map"; import { PipeLayer } from "./_private/PipeLayer"; +import { getMd } from "./_private/wellTrajectoryUtils"; export type WellsLayerData = { coordinates: [number, number, number][]; - properties: { uuid: string; name: string }; + properties: { uuid: string; name: string; mdArray: number[] }; }[]; export interface WellsLayerProps extends ExtendedLayerProps { @@ -23,6 +26,12 @@ export interface WellsLayerProps extends ExtendedLayerProps { export class WellsLayer extends CompositeLayer { static layerName: string = "WellsLayer"; + // @ts-expect-error - This is how deck.gl expects the state to be defined + state!: { + hoveredPipeIndex: number | null; + mdCoordinate: [number, number, number] | null; + }; + updateState(params: any): void { super.updateState(params); @@ -33,16 +42,93 @@ export class WellsLayer extends CompositeLayer { }); } + getPickingInfo({ info }: GetPickingInfoParams): LayerPickInfo { + if (!info.sourceLayer?.id.includes("pipe-layer")) { + return info; + } + + const wellbore = this.props.data[info.index]; + if (!wellbore) { + return info; + } + info.object = this.props.data[info.index]; + const coordinate = info.coordinate ?? [0, 0, 0]; + + const zScale = this.props.modelMatrix ? this.props.modelMatrix[10] : 1; + if (typeof coordinate[2] !== "undefined") { + coordinate[2] /= Math.max(0.001, zScale); + } + + const md = getMd( + vec3.fromArray(coordinate), + wellbore.properties.mdArray, + wellbore.coordinates.map((coord) => vec3.fromArray(coord)) + ); + + if (md !== null) { + this.setState({ mdCoordinate: coordinate }); + return { + ...info, + properties: [ + { + name: `MD ${wellbore.properties.name}`, + value: md, + }, + ], + }; + } + + return info; + } + + onHover(info: PickingInfo): boolean { + if (!info.sourceLayer) { + return false; + } + + const { sourceLayer } = info; + if (sourceLayer.id !== "hover-path-layer") { + return false; + } + + const { index } = info; + this.setState({ hoveredPipeIndex: index }); + return false; + } + renderLayers(): LayersList { return [ + new PointCloudLayer({ + id: "hover-md-layer", + data: this.state.mdCoordinate ? [this.state.mdCoordinate] : [], + getPosition: (d: any) => d, + getColor: [0, 255, 0], + getRadius: 10, + pickable: false, + billboard: true, + }), + new PathLayer({ + id: "path-layer", + data: this.props.data.map((well) => well.coordinates.map((coord) => [coord[0], coord[1], coord[2]])), + getPath: (d: any) => d, + getColor: [255, 0, 0], + getWidth: 5, + widthUnits: "meters", + pickable: false, + billboard: true, + }), new PipeLayer({ - id: "pipelayer", + id: "pipe-layer", data: this.props.data.map((well) => { - return well.coordinates.map((coord) => { - return { x: coord[0], y: coord[1], z: coord[2] }; - }); + return { + id: well.properties.uuid, + centerLinePath: well.coordinates.map((coord) => { + return { x: coord[0], y: coord[1], z: coord[2] }; + }), + }; }), material: true, + pickable: true, }), ]; } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts index 69bee59a0..db45bcab3 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts @@ -1,42 +1,96 @@ -import { Layer, LayerContext, Material, UpdateParameters, picking, project32 } from "@deck.gl/core"; +import { + GetPickingInfoParams, + Layer, + LayerContext, + LayerProps, + Material, + PickingInfo, + UpdateParameters, + picking, + project32, +} from "@deck.gl/core"; import { Vec3 } from "@lib/utils/vec3"; import * as vec3 from "@lib/utils/vec3"; import { Geometry, Model } from "@luma.gl/engine"; import { phongLighting } from "@luma.gl/shadertools"; +import { isEqual } from "lodash"; + import { Pipe } from "./Pipe"; import fragmentShader from "./shaders/fragmentShader.glsl?raw"; +import { PipeProps, pipeUniforms } from "./shaders/uniforms"; import vertexShader from "./shaders/vertexShader.glsl?raw"; export type PipeLayerProps = { id: string; - data: Vec3[][]; + data: { + id: string; + centerLinePath: Vec3[]; + }[]; + color: [number, number, number, number]; + hoverColor: [number, number, number, number]; + selectedColor: [number, number, number, number]; + hoveredPipeIndex: number | null; + selectedPipeIndex: number | null; material?: Material; -}; +} & LayerProps; export class PipeLayer extends Layer { static layerName: string = "PipeLayer"; - // @ts-ignore - This is how deck.gl expects the state to be defined + // @ts-expect-error - This is how deck.gl expects the state to be defined state!: { models: Model[]; + hoveredPipeIndex: number | null; }; initializeState(context: LayerContext): void { - this.state = { + this.setState({ models: this.makeModels(context), + hoveredPipeIndex: null, + }); + } + + getPickingInfo(params: GetPickingInfoParams): PickingInfo { + const info = super.getPickingInfo(params); + + if (!info.color) { + this.setState({ hoveredPipeIndex: null }); + return info; + } + + const r = info.color[0]; + const g = info.color[1]; + const b = info.color[2]; + + const pipeIndex = r * 256 * 256 + g * 256 + b; + + this.setState({ hoveredPipeIndex: pipeIndex }); + + return { + ...info, + picked: true, + index: pipeIndex, + object: this.props.data[pipeIndex], }; } draw(): void { - const { models } = this.state; - for (const model of models) { + const { models, hoveredPipeIndex } = this.state; + for (const [idx, model] of models.entries()) { + model.shaderInputs.setProps({ + pipe: { + isHovered: hoveredPipeIndex === idx, + }, + }); model.draw(this.context.renderPass); } } updateState(params: UpdateParameters>): void { - if (params.changeFlags.dataChanged) { + super.updateState(params); + + if (!isEqual(this.props.data, params.props.data)) { this.setState({ models: this.makeModels(params.context), }); @@ -49,15 +103,20 @@ export class PipeLayer extends Layer { const models: Model[] = []; for (const [idx, mesh] of meshes.entries()) { - models.push( - new Model(context.device, { - id: `${this.id}-mesh-${idx}`, - geometry: mesh, - modules: [project32, phongLighting, picking], - vs: vertexShader, - fs: fragmentShader, - }) - ); + const pipeProps: PipeProps = { + pipeIndex: idx, + isHovered: false, + }; + const model = new Model(context.device, { + id: `${this.id}-mesh-${idx}`, + geometry: mesh, + modules: [project32, phongLighting, picking, pipeUniforms], + vs: vertexShader, + fs: fragmentShader, + }); + model.shaderInputs.setProps({ pipe: pipeProps }); + + models.push(model); } return models; @@ -68,8 +127,8 @@ export class PipeLayer extends Layer { const circle = this.makeCircle(10, 16); const pipes: Pipe[] = []; - for (const points of data) { - pipes.push(new Pipe(points, circle)); + for (const pipeData of data) { + pipes.push(new Pipe(pipeData.centerLinePath, circle)); } return pipes; @@ -91,8 +150,10 @@ export class PipeLayer extends Layer { } private makeMeshes(pipes: Pipe[]): Geometry[] { + const { data } = this.props; + const geometries: Geometry[] = []; - for (const pipe of pipes) { + for (const [idx, pipe] of pipes.entries()) { const numContours = pipe.getContourCount(); const numVerticesPerContour = pipe.getContours()[0].length; @@ -209,6 +270,7 @@ export class PipeLayer extends Layer { }, }, indices: indices, + id: data[idx].id, }) ); } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl index d69e686d2..52dae0fce 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/fragmentShader.glsl @@ -6,14 +6,52 @@ precision highp float; in vec3 cameraPosition; in vec4 position_commonspace; flat in vec3 normal; +in float pointingTowardsCamera; out vec4 fragColor; +vec4 encodeVertexAndPipeIndexToRGB(float pipeIndex) { + int index = int(pipeIndex); + float r = 0.0f; + float g = 0.0f; + float b = 0.0f; + + if(index >= (256 * 256) - 1) { + r = floor(float(index) / (256.0f * 256.0f)); + index -= int(r * (256.0f * 256.0f)); + } + + if(index >= 256 - 1) { + g = floor(float(index) / 256.0f); + index -= int(g * 256.0f); + } + + b = float(index); + + return vec4(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f); +} + void main(void) { + if(picking.isActive > 0.5f && !(picking.isAttribute > 0.5f)) { + float pipeIndex = pipe.pipeIndex; + fragColor = encodeVertexAndPipeIndexToRGB(pipeIndex); + return; + } + vec4 color = vec4(0.85f, 0.85f, 0.85f, 1.0f); - geometry.uv = vec2(0.); - + if(pipe.isHovered) { + color = vec4(0.42f, 0.47f, 0.88f, 1.0f); + /* + This could be a nice effect, but it's not used in the current implementation + if(pointingTowardsCamera > 0.0f) { + color.a = 0.2f; + } + */ + } + + geometry.uv = vec2(0.f); + DECKGL_FILTER_COLOR(color, geometry); vec3 lightColor = lighting_getLightColor(color.rgb, cameraPosition, position_commonspace.xyz, normal); fragColor = vec4(lightColor, color.a); diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/uniforms.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/uniforms.ts new file mode 100644 index 000000000..94333492a --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/uniforms.ts @@ -0,0 +1,23 @@ +import type { ShaderModule } from "@luma.gl/shadertools"; + +const uniformBlock = `\ +uniform pipeUniforms { + float pipeIndex; + bool isHovered; +} pipe; + `; + +export type PipeProps = { + pipeIndex?: number; + isHovered?: boolean; +}; + +export const pipeUniforms = { + name: "pipe", + vs: uniformBlock, + fs: uniformBlock, + uniformTypes: { + pipeIndex: "f32", + isHovered: "f32", + }, +} as const satisfies ShaderModule; diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl index 63ba5d483..eb909b8b3 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl @@ -1,27 +1,32 @@ #version 300 es #define SHADER_NAME wells-layer-vertex-shader +precision highp float; + in vec3 positions; in vec3 normals; flat out vec3 normal; out vec3 cameraPosition; out vec4 position_commonspace; +out float pointingTowardsCamera; -void main(void) -{ +void main(void) { cameraPosition = project_uCameraPosition; normal = normals; - position_commonspace = vec4(project_position(positions), 0.0); + vec3 cameraPositionToPosition = normalize(cameraPosition - positions); + pointingTowardsCamera = dot(cameraPositionToPosition, normals); + + position_commonspace = vec4(project_position(positions), 0.0f); geometry.position = position_commonspace; gl_Position = project_common_position_to_clipspace(position_commonspace); - + DECKGL_FILTER_GL_POSITION(gl_Position, geometry); - vec4 color = vec4(0.0); + vec4 color = vec4(0.0f); DECKGL_FILTER_COLOR(color, geometry); } \ No newline at end of file diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/wellTrajectoryUtils.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/wellTrajectoryUtils.ts new file mode 100644 index 000000000..0a4f1a990 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/wellTrajectoryUtils.ts @@ -0,0 +1,76 @@ +import * as vec3 from "@lib/utils/vec3"; + +function squared_distance(a: vec3.Vec3, b: vec3.Vec3): number { + const dx = a.x - b.x; + const dy = a.y - b.y; + const dz = a.z - b.z; + return dx * dx + dy * dy + dz * dz; +} + +function distPointToSegmentSquared(segment: { v: vec3.Vec3; w: vec3.Vec3 }, point: vec3.Vec3): number { + const l2 = squared_distance(segment.v, segment.w); + if (l2 === 0) return squared_distance(point, segment.v); + let t = + ((point.x - segment.v.x) * (segment.w.x - segment.v.x) + + (point.y - segment.v.y) * (segment.w.y - segment.v.y) + + (point.z - segment.v.z) * (segment.w.z - segment.v.z)) / + l2; + t = Math.max(0, Math.min(1, t)); + return squared_distance(point, { + x: segment.v.x + t * (segment.w.x - segment.v.x), + y: segment.v.y + t * (segment.w.y - segment.v.y), + z: segment.v.z + t * (segment.w.z - segment.v.z), + }); +} + +function getSegmentIndex(coord: vec3.Vec3, path: vec3.Vec3[]): number { + let minD = Number.MAX_VALUE; + let segmentIndex = 0; + for (let i = 0; i < path.length - 1; i++) { + const d = distPointToSegmentSquared({ v: path[i], w: path[i + 1] }, coord); + if (d > minD) continue; + + segmentIndex = i; + minD = d; + } + return segmentIndex; +} + +function interpolateDataOnTrajectory(coord: vec3.Vec3, data: number[], trajectory: vec3.Vec3[]): number { + // if number of data points in less than 1 or + // length of data and trajectory are different we cannot interpolate. + if (data.length <= 1 || data.length != trajectory.length) return -1; + + // Identify closest well path leg to coord. + const segmentIndex = getSegmentIndex(coord, trajectory); + + const index0 = segmentIndex; + const index1 = index0 + 1; + + // Get the nearest data. + const data0 = data[index0]; + const data1 = data[index1]; + + // Get the nearest survey points. + const survey0 = trajectory[index0]; + const survey1 = trajectory[index1]; + + const dv = vec3.distance(survey0, survey1) as number; + if (dv === 0) { + return -1; + } + + // Calculate the scalar projection onto segment. + const v0 = vec3.subtract(coord, survey0); + const v1 = vec3.subtract(survey1, survey0); + + // scalar_projection in interval [0,1] + const scalar_projection: number = vec3.dot(v0, v1) / (dv * dv); + + // Interpolate data. + return data0 * (1.0 - scalar_projection) + data1 * scalar_projection; +} + +export function getMd(coord: vec3.Vec3, mdArray: number[], trajectory: vec3.Vec3[]): number | null { + return interpolateDataOnTrajectory(coord, mdArray, trajectory); +} From 3381510ae0bcbc43bc71edc1836026b0fb56bdc7 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 25 Feb 2025 23:49:56 +0100 Subject: [PATCH 63/97] wip --- .../WellsLayer/WellsLayer.ts | 32 ++++++++++------- .../_private/shaders/vertexShader.glsl | 3 ++ .../_private/wellTrajectoryUtils.ts | 34 +++++++++++++++++++ 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts index 2fc48371c..d0ef54546 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts @@ -5,7 +5,7 @@ import { ExtendedLayerProps, LayerPickInfo } from "@webviz/subsurface-viewer"; import { BoundingBox3D, ReportBoundingBoxAction } from "@webviz/subsurface-viewer/dist/components/Map"; import { PipeLayer } from "./_private/PipeLayer"; -import { getMd } from "./_private/wellTrajectoryUtils"; +import { getCoordinateForMd, getMd } from "./_private/wellTrajectoryUtils"; export type WellsLayerData = { coordinates: [number, number, number][]; @@ -44,29 +44,31 @@ export class WellsLayer extends CompositeLayer { getPickingInfo({ info }: GetPickingInfoParams): LayerPickInfo { if (!info.sourceLayer?.id.includes("pipe-layer")) { + this.setState({ mdCoordinate: null }); + return info; } const wellbore = this.props.data[info.index]; if (!wellbore) { + this.setState({ mdCoordinate: null }); return info; } info.object = this.props.data[info.index]; + const coordinate = info.coordinate ?? [0, 0, 0]; - const zScale = this.props.modelMatrix ? this.props.modelMatrix[10] : 1; - if (typeof coordinate[2] !== "undefined") { - coordinate[2] /= Math.max(0.001, zScale); - } + const trajectory = wellbore.coordinates.map((coord) => vec3.fromArray(coord)); - const md = getMd( - vec3.fromArray(coordinate), - wellbore.properties.mdArray, - wellbore.coordinates.map((coord) => vec3.fromArray(coord)) - ); + const md = getMd(vec3.fromArray(coordinate), wellbore.properties.mdArray, trajectory); if (md !== null) { - this.setState({ mdCoordinate: coordinate }); + const mdCoordinate = getCoordinateForMd(md, wellbore.properties.mdArray, trajectory); + if (mdCoordinate === null) { + this.setState({ mdCoordinate: null }); + } else { + this.setState({ mdCoordinate: [mdCoordinate?.x ?? 0, mdCoordinate?.y ?? 0, mdCoordinate?.z ?? 0] }); + } return { ...info, properties: [ @@ -77,6 +79,7 @@ export class WellsLayer extends CompositeLayer { ], }; } + this.setState({ mdCoordinate: null }); return info; } @@ -102,10 +105,11 @@ export class WellsLayer extends CompositeLayer { id: "hover-md-layer", data: this.state.mdCoordinate ? [this.state.mdCoordinate] : [], getPosition: (d: any) => d, - getColor: [0, 255, 0], - getRadius: 10, + getColor: [255, 0, 0], + getRadius: 5, pickable: false, billboard: true, + visible: this.state.mdCoordinate !== null, }), new PathLayer({ id: "path-layer", @@ -129,6 +133,8 @@ export class WellsLayer extends CompositeLayer { }), material: true, pickable: true, + // @ts-expect-error - This is how deck.gl expects the state to be defined + parameters: { depthTest: true }, }), ]; } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl index eb909b8b3..8cc7cb0c1 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/shaders/vertexShader.glsl @@ -11,6 +11,8 @@ out vec3 cameraPosition; out vec4 position_commonspace; out float pointingTowardsCamera; +const vec3 pickingColor = vec3(1.0, 1.0, 0.0); + void main(void) { cameraPosition = project_uCameraPosition; @@ -22,6 +24,7 @@ void main(void) { position_commonspace = vec4(project_position(positions), 0.0f); geometry.position = position_commonspace; + geometry.pickingColor = pickingColor; gl_Position = project_common_position_to_clipspace(position_commonspace); diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/wellTrajectoryUtils.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/wellTrajectoryUtils.ts index 0a4f1a990..dbe581e6d 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/wellTrajectoryUtils.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/wellTrajectoryUtils.ts @@ -74,3 +74,37 @@ function interpolateDataOnTrajectory(coord: vec3.Vec3, data: number[], trajector export function getMd(coord: vec3.Vec3, mdArray: number[], trajectory: vec3.Vec3[]): number | null { return interpolateDataOnTrajectory(coord, mdArray, trajectory); } + +export function getCoordinateForMd(md: number, mdArray: number[], trajectory: vec3.Vec3[]): vec3.Vec3 | null { + const numPoints = mdArray.length; + if (numPoints < 2) { + return null; + } + + let segmentIndex = 0; + for (let i = 0; i < numPoints - 1; i++) { + if (mdArray[i] <= md && md <= mdArray[i + 1]) { + segmentIndex = i; + break; + } + } + + const md0 = mdArray[segmentIndex]; + const md1 = mdArray[segmentIndex + 1]; + + const survey0 = trajectory[segmentIndex]; + const survey1 = trajectory[segmentIndex + 1]; + + const dv = vec3.distance(survey0, survey1) as number; + if (dv === 0) { + return null; + } + + const scalar_projection = (md - md0) / (md1 - md0); + + return { + x: survey0.x + scalar_projection * (survey1.x - survey0.x), + y: survey0.y + scalar_projection * (survey1.y - survey0.y), + z: survey0.z + scalar_projection * (survey1.z - survey0.z), + }; +} From 19b6d0efbd94d58896598d82fa94b3dc3218dd01 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 25 Feb 2025 23:54:20 +0100 Subject: [PATCH 64/97] wip --- .../WellsLayer/WellsLayer.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts index d0ef54546..3dc0a0a4b 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts @@ -101,16 +101,6 @@ export class WellsLayer extends CompositeLayer { renderLayers(): LayersList { return [ - new PointCloudLayer({ - id: "hover-md-layer", - data: this.state.mdCoordinate ? [this.state.mdCoordinate] : [], - getPosition: (d: any) => d, - getColor: [255, 0, 0], - getRadius: 5, - pickable: false, - billboard: true, - visible: this.state.mdCoordinate !== null, - }), new PathLayer({ id: "path-layer", data: this.props.data.map((well) => well.coordinates.map((coord) => [coord[0], coord[1], coord[2]])), @@ -136,6 +126,17 @@ export class WellsLayer extends CompositeLayer { // @ts-expect-error - This is how deck.gl expects the state to be defined parameters: { depthTest: true }, }), + new PointCloudLayer({ + id: "hover-md-layer", + data: this.state.mdCoordinate ? [this.state.mdCoordinate] : [], + getPosition: (d: any) => d, + getColor: [255, 0, 0], + getRadius: 30, + sizeUnits: "meters", + pickable: false, + billboard: true, + visible: this.state.mdCoordinate !== null, + }), ]; } } From 0a5e0dae8e4f78c6b0b3f9e4a8c41a6322236457 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 26 Feb 2025 09:57:22 +0100 Subject: [PATCH 65/97] wip --- .../WellsLayer/WellsLayer.ts | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts index 3dc0a0a4b..cedccd137 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts @@ -1,5 +1,4 @@ import { CompositeLayer, GetPickingInfoParams, LayersList, PickingInfo } from "@deck.gl/core"; -import { PathLayer, PointCloudLayer } from "@deck.gl/layers"; import * as vec3 from "@lib/utils/vec3"; import { ExtendedLayerProps, LayerPickInfo } from "@webviz/subsurface-viewer"; import { BoundingBox3D, ReportBoundingBoxAction } from "@webviz/subsurface-viewer/dist/components/Map"; @@ -101,16 +100,6 @@ export class WellsLayer extends CompositeLayer { renderLayers(): LayersList { return [ - new PathLayer({ - id: "path-layer", - data: this.props.data.map((well) => well.coordinates.map((coord) => [coord[0], coord[1], coord[2]])), - getPath: (d: any) => d, - getColor: [255, 0, 0], - getWidth: 5, - widthUnits: "meters", - pickable: false, - billboard: true, - }), new PipeLayer({ id: "pipe-layer", data: this.props.data.map((well) => { @@ -126,17 +115,6 @@ export class WellsLayer extends CompositeLayer { // @ts-expect-error - This is how deck.gl expects the state to be defined parameters: { depthTest: true }, }), - new PointCloudLayer({ - id: "hover-md-layer", - data: this.state.mdCoordinate ? [this.state.mdCoordinate] : [], - getPosition: (d: any) => d, - getColor: [255, 0, 0], - getRadius: 30, - sizeUnits: "meters", - pickable: false, - billboard: true, - visible: this.state.mdCoordinate !== null, - }), ]; } } From 18e81ffafb78a48302bcf7a03c8e637da6e41363 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 26 Mar 2025 17:17:53 +0100 Subject: [PATCH 66/97] wip --- frontend/package-lock.json | 60 +++++ .../Content/private-components/layout.tsx | 2 +- frontend/src/lib/utils/mat3.ts | 30 ++- frontend/src/lib/utils/vec3.ts | 184 +++++++++++++ .../customLayerImplementations/layerTypes.ts | 7 - .../registerAllLayers.ts | 14 - .../layerManagerComponentWrapper.tsx | 31 +-- .../view/components/LayersWrapper.tsx | 40 ++- .../IntersectionRealizationGridLayer.ts | 213 --------------- ...ersectionRealizationGridSettingsContext.ts | 212 --------------- .../IntersectionRealizationGridLayer/index.ts | 2 - .../IntersectionRealizationGridLayer/types.ts | 13 - .../RealizationGridLayer.ts | 187 ------------- .../RealizationGridSettingsContext.ts | 218 ---------------- .../RealizationGridLayer/index.ts | 2 - .../RealizationGridLayer/types.ts | 14 - .../RealizationSeismicCrosslineLayer.ts | 214 +++++++++++++++ .../RealizationSeismicCrosslineLayer.ts | 190 -------------- ...lizationSeismicCrosslineSettingsContext.ts | 166 ------------ .../RealizationSeismicCrosslineLayer/index.ts | 2 - .../RealizationSeismicCrosslineLayer/types.ts | 15 -- .../RealizationSeismicDepthLayer.ts | 217 ++++++++++++++++ .../RealizationSeismicDepthSliceLayer.ts | 212 --------------- ...izationSeismicDepthSliceSettingsContext.ts | 172 ------------ .../index.ts | 2 - .../types.ts | 15 -- .../RealizationSeismicInlineLayer.ts | 214 +++++++++++++++ .../RealizationSeismicInlineLayer.ts | 199 -------------- ...RealizationSeismicInlineSettingsContext.ts | 164 ------------ .../RealizationSeismicInlineLayer/index.ts | 2 - .../RealizationSeismicInlineLayer/types.ts | 15 -- .../RealizationSurfaceLayer.ts | 131 ---------- .../RealizationSurfaceSettingsContext.ts | 149 ----------- .../RealizationSurfaceLayer/index.ts | 2 - .../RealizationSurfaceLayer/types.ts | 10 - .../visualization/makeGrid3dLayer.ts | 37 --- .../makeIntersectionGrid3dLayer.ts | 24 +- .../makeRealizationSurfaceLayer.ts | 71 ++--- .../makeSeismicFenceMeshLayer.ts | 245 +++++++++++++++++- .../src/modules/3DViewerNew/interfaces.ts | 4 +- .../3DViewerNew/settings/atoms/baseAtoms.ts | 4 +- .../layerManagerComponentWrapper.tsx | 120 +++++---- .../modules/3DViewerNew/settings/settings.tsx | 23 +- .../view/components/LayersWrapper.tsx | 134 +++++++--- .../customDataLayerImplementation.ts | 10 +- .../IntersectionRealizationGridLayer.ts | 30 ++- .../implementations}/ObservedSurfaceLayer.ts | 0 .../implementations}/RealizationGridLayer.ts | 89 ++++--- .../RealizationPolygonsLayer.ts | 0 .../RealizationSurfaceLayer.ts | 10 +- .../StatisticalSurfaceLayer.ts | 0 .../LayerFramework/layers/layerTypes.ts | 9 + .../layers/registerAllLayers.ts | 19 ++ .../GridLayerIRangeSetting.tsx | 93 ------- .../GridLayerJRangeSetting.tsx | 93 ------- .../GridLayerKRangeSetting.tsx | 93 ------- .../SeismicCrosslineSetting.tsx | 106 -------- .../implementations/SeismicInlineSetting.tsx | 106 -------- .../visualization/VisualizationFactory.ts | 99 ++++--- .../makePolygonDataBoundingBox.ts | 0 .../makeRealizationGridBoundingBox.ts | 2 +- .../makeSurfaceLayerBoundingBox.ts | 2 +- .../deckgl}/makeObservedSurfaceLayer.ts | 6 +- .../deckgl}/makeRealizationGridLayer.ts | 4 +- .../deckgl}/makeRealizationPolygonsLayer.ts | 2 +- .../deckgl}/makeRealizationSurfaceLayer.ts | 6 +- .../deckgl}/makeStatisticalSurfaceLayer.ts | 6 +- .../deckgl/makeWellborePicksLayer.ts | 27 -- .../visualization/deckgl/makeWellsLayer.ts | 122 --------- .../PlaceholderLayer.ts~HEAD | 21 ++ .../PlaceholderLayer.ts~main | 21 ++ .../customDeckGlLayers/WellborePicksLayer.ts | 4 +- 72 files changed, 1634 insertions(+), 3328 deletions(-) delete mode 100644 frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/layerTypes.ts delete mode 100644 frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/registerAllLayers.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/index.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/types.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/index.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/index.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/index.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/index.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer.ts rename frontend/src/modules/{2DViewer/LayerFramework/customLayerImplementations => _shared/LayerFramework/layers/implementations}/ObservedSurfaceLayer.ts (100%) rename frontend/src/modules/{2DViewer/LayerFramework/customLayerImplementations => _shared/LayerFramework/layers/implementations}/RealizationGridLayer.ts (83%) rename frontend/src/modules/{2DViewer/LayerFramework/customLayerImplementations => _shared/LayerFramework/layers/implementations}/RealizationPolygonsLayer.ts (100%) rename frontend/src/modules/{2DViewer/LayerFramework/customLayerImplementations => _shared/LayerFramework/layers/implementations}/RealizationSurfaceLayer.ts (97%) rename frontend/src/modules/{2DViewer/LayerFramework/customLayerImplementations => _shared/LayerFramework/layers/implementations}/StatisticalSurfaceLayer.ts (100%) delete mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting.tsx delete mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting.tsx delete mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting.tsx delete mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx delete mode 100644 frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx rename frontend/src/modules/{2DViewer/LayerFramework => _shared/LayerFramework/visualization/deckgl}/boundingBoxes/makePolygonDataBoundingBox.ts (100%) rename frontend/src/modules/{2DViewer/LayerFramework => _shared/LayerFramework/visualization/deckgl}/boundingBoxes/makeRealizationGridBoundingBox.ts (89%) rename frontend/src/modules/{2DViewer/LayerFramework => _shared/LayerFramework/visualization/deckgl}/boundingBoxes/makeSurfaceLayerBoundingBox.ts (87%) rename frontend/src/modules/{2DViewer/LayerFramework/visualization => _shared/LayerFramework/visualization/deckgl}/makeObservedSurfaceLayer.ts (95%) rename frontend/src/modules/{2DViewer/LayerFramework/visualization => _shared/LayerFramework/visualization/deckgl}/makeRealizationGridLayer.ts (94%) rename frontend/src/modules/{2DViewer/LayerFramework/visualization => _shared/LayerFramework/visualization/deckgl}/makeRealizationPolygonsLayer.ts (96%) rename frontend/src/modules/{2DViewer/LayerFramework/visualization => _shared/LayerFramework/visualization/deckgl}/makeRealizationSurfaceLayer.ts (95%) rename frontend/src/modules/{2DViewer/LayerFramework/visualization => _shared/LayerFramework/visualization/deckgl}/makeStatisticalSurfaceLayer.ts (95%) delete mode 100644 frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer.ts delete mode 100644 frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/PlaceholderLayer.ts~HEAD create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/PlaceholderLayer.ts~main diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 77afd8640..fa97d02a7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -5444,6 +5444,49 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.34.8", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", @@ -17144,6 +17187,23 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/vite-plugin-glsl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vite-plugin-glsl/-/vite-plugin-glsl-1.3.1.tgz", + "integrity": "sha512-iClII8Idb9X0m6nS0YI2cWWXbBuT5EKKw5kXSAuRu4RJsNe4oypxKXE7jx0XMoyqij2s8WL0ZLfou801mpkREg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">= 16.15.1", + "npm": ">= 8.11.0" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + } + }, "node_modules/vitest": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.7.tgz", diff --git a/frontend/src/framework/internal/components/Content/private-components/layout.tsx b/frontend/src/framework/internal/components/Content/private-components/layout.tsx index 069376d38..40bd9c35e 100644 --- a/frontend/src/framework/internal/components/Content/private-components/layout.tsx +++ b/frontend/src/framework/internal/components/Content/private-components/layout.tsx @@ -10,7 +10,7 @@ import { useElementSize } from "@lib/hooks/useElementSize"; import type { Rect2D, Size2D } from "@lib/utils/geometry"; import { MANHATTAN_LENGTH, addMarginToRect, pointRelativeToDomRect, rectContainsPoint } from "@lib/utils/geometry"; import type { Vec2 } from "@lib/utils/vec2"; -import { multiplyVec2, point2Distance, scaleVec2NonUniform, subtractVec2, vec2FromPointerEvent } from "@lib/utils/vec2"; +import { multiplyElementWiseVec2, point2Distance, scaleVec2NonUniform, subtractVec2, vec2FromPointerEvent } from "@lib/utils/vec2"; import { v4 } from "uuid"; diff --git a/frontend/src/lib/utils/mat3.ts b/frontend/src/lib/utils/mat3.ts index 699fde847..6864161b7 100644 --- a/frontend/src/lib/utils/mat3.ts +++ b/frontend/src/lib/utils/mat3.ts @@ -1,3 +1,6 @@ +/** + * A 3x3 matrix. + */ export type Mat3 = { m00: number; m01: number; @@ -10,6 +13,11 @@ export type Mat3 = { m22: number; }; +/** + * Creates a new 3x3 matrix with all elements set to 0. + * + * @returns A new 3x3 matrix. + */ export function createEmpty(): Mat3 { return { m00: 0, @@ -24,6 +32,20 @@ export function createEmpty(): Mat3 { }; } +/** + * Creates a new 3x3 matrix with the given elements. + * + * @param m00 The element at row 0, column 0. + * @param m01 The element at row 0, column 1. + * @param m02 The element at row 0, column 2. + * @param m10 The element at row 1, column 0. + * @param m11 The element at row 1, column 1. + * @param m12 The element at row 1, column 2. + * @param m20 The element at row 2, column 0. + * @param m21 The element at row 2, column 1. + * @param m22 The element at row 2, column 2. + * @returns A new 3x3 matrix. + */ export function create( m00: number, m01: number, @@ -38,6 +60,12 @@ export function create( return { m00, m01, m02, m10, m11, m12, m20, m21, m22 }; } +/** + * Creates a new 3x3 matrix from the given array of numbers. + * + * @param array An array of numbers in the following order: [m00, m01, m02, m10, m11, m12, m20, m21, m22]. + * @returns A new 3x3 matrix. + */ export function fromArray( array: ArrayLike | [number, number, number, number, number, number, number, number, number] ): Mat3 { @@ -52,4 +80,4 @@ export function fromArray( m21: array[7], m22: array[8], }; -} +} \ No newline at end of file diff --git a/frontend/src/lib/utils/vec3.ts b/frontend/src/lib/utils/vec3.ts index 7105315cd..3beaa69a6 100644 --- a/frontend/src/lib/utils/vec3.ts +++ b/frontend/src/lib/utils/vec3.ts @@ -1,3 +1,5 @@ +import { Mat3 } from "./mat3"; + /** * A 3D vector. */ @@ -51,3 +53,185 @@ export function toArray(vector: Vec3): [number, number, number] { export function clone(vector: Vec3): Vec3 { return { x: vector.x, y: vector.y, z: vector.z }; } + +/** + * Calculates the length of the given vector. + * + * @param vector The vector. + * @returns The length of the vector. + */ +export function length(vector: Vec3): number { + return Math.sqrt(vector.x ** 2 + vector.y ** 2 + vector.z ** 2); +} + +/** + * Calculates the squared length of the given vector. + * + * @param vector The vector. + * @returns The squared length of the vector. + */ +export function squaredLength(vector: Vec3): number { + return vector.x ** 2 + vector.y ** 2 + vector.z ** 2; +} + +/** + * Calculates the distance between two points. + * + * @param point1 The first point. + * @param point2 The second point. + * @returns The distance between the two points. + */ +export function distance(point1: Vec3, point2: Vec3): number { + return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2 + (point1.z - point2.z) ** 2); +} + +/** + * Calculates the squared distance between two points. + * + * @param point1 The first point. + * @param point2 The second point. + * @returns The squared distance between the two points. + */ +export function squaredDistance(point1: Vec3, point2: Vec3): number { + return (point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2 + (point1.z - point2.z) ** 2; +} + +/** + * Subtracts the subtrahend from the minuend. + * + * @param minuend The minuend. + * @param subtrahend The subtrahend. + * @returns A new vector that is the result of the subtraction. + */ +export function subtract(minuend: Vec3, subtrahend: Vec3): Vec3 { + return { x: minuend.x - subtrahend.x, y: minuend.y - subtrahend.y, z: minuend.z - subtrahend.z }; +} + +/** + * Adds two vectors. + * + * @param vector1 The first vector. + * @param vector2 The second vector. + * @returns A new vector that is the result of the addition. + */ +export function add(vector1: Vec3, vector2: Vec3): Vec3 { + return { x: vector1.x + vector2.x, y: vector1.y + vector2.y, z: vector1.z + vector2.z }; +} + +/** + * Normalizes the given vector. + * + * @param vector The vector. + * @returns A new vector that is the normalized version of the given vector. + */ +export function normalize(vector: Vec3): Vec3 { + const len = length(vector); + return { x: vector.x / len, y: vector.y / len, z: vector.z / len }; +} + +/** + * Negates the given vector. + * + * @param vector The vector. + * @returns A new vector that is the negated version of the given vector. + */ +export function negate(vector: Vec3): Vec3 { + return { x: -vector.x, y: -vector.y, z: -vector.z }; +} + +/** + * Returns the absolute values of the components of the given vector. + * + * @param vector The vector. + * @returns A new vector that is the absolute version of the given vector. + */ +export function abs(vector: Vec3): Vec3 { + return { x: Math.abs(vector.x), y: Math.abs(vector.y), z: Math.abs(vector.z) }; +} + +/** + * Scales the given vector by the given scalar. + * + * @param vector The vector. + * @param scalar The scalar. + * @returns A new vector that is the result of the scaling. + */ +export function scale(vector: Vec3, scalar: number): Vec3 { + return { x: vector.x * scalar, y: vector.y * scalar, z: vector.z * scalar }; +} + +/** + * Scales the given vector components by the respective given scalar. + * + * @param vector The vector. + * @param scalarX The scalar for the x component. + * @param scalarY The scalar for the y component. + * @param scalarZ The scalar for the z component. + * @returns A new vector that is the result of the scaling. + */ +export function scaleNonUniform(vector: Vec3, scalarX: number, scalarY: number, scalarZ: number): Vec3 { + return { x: vector.x * scalarX, y: vector.y * scalarY, z: vector.z * scalarZ }; +} + +/** + * Multiplies two vectors element-wise. + * + * @param vecA The first vector. + * @param vecB The second vector. + * @returns A new vector that is the result of the element-wise multiplication. + */ +export function multiplyElementWise(vecA: Vec3, vecB: Vec3): Vec3 { + return { x: vecA.x * vecB.x, y: vecA.y * vecB.y, z: vecA.z * vecB.z }; +} + +/** + * Returns true if the two vectors are equal. + * + * @param vector1 The first vector. + * @param vector2 The second vector. + * @returns True if the two vectors are equal. + */ +export function equal(vector1: Vec3, vector2: Vec3): boolean { + return vector1.x === vector2.x && vector1.y === vector2.y && vector1.z === vector2.z; +} + +/** + * Calculates the dot product of two vectors. + * + * @param vector1 The first vector. + * @param vector2 The second vector. + * @returns The dot product of the two vectors. + */ +export function dot(vector1: Vec3, vector2: Vec3): number { + return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z; +} + +/** + * Calculates the cross product of two vectors. + * + * @param vector1 The first vector. + * @param vector2 The second vector. + * @returns A new vector that is the cross product of the two vectors. + */ +export function cross(vector1: Vec3, vector2: Vec3): Vec3 { + return { + x: vector1.y * vector2.z - vector1.z * vector2.y, + y: vector1.z * vector2.x - vector1.x * vector2.z, + z: vector1.x * vector2.y - vector1.y * vector2.x, + }; +} + +/** + * Transforms the given vector by the given matrix. + * + * @param vector A 3D vector to transform. + * @param matrix A 3x3 matrix to transform the vector with. + * @returns A new 3D vector that is the result of the transformation. + */ +export function transform(vector: Vec3, matrix: Mat3): Vec3 { + return { + x: matrix.m00 * vector.x + matrix.m01 * vector.y + matrix.m02 * vector.z, + y: matrix.m10 * vector.x + matrix.m11 * vector.y + matrix.m12 * vector.z, + z: matrix.m20 * vector.x + matrix.m21 * vector.y + matrix.m22 * vector.z, + }; +} \ No newline at end of file diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/layerTypes.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/layerTypes.ts deleted file mode 100644 index 331a81e92..000000000 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/layerTypes.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum CustomLayerType { - OBSERVED_SURFACE = "OBSERVED_SURFACE", - REALIZATION_SURFACE = "REALIZATION_SURFACE", - STATISTICAL_SURFACE = "STATISTICAL_SURFACE", - REALIZATION_POLYGONS = "REALIZATION_POLYGONS", - REALIZATION_GRID = "REALIZATION_GRID", -} diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/registerAllLayers.ts b/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/registerAllLayers.ts deleted file mode 100644 index d2d3817ee..000000000 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/registerAllLayers.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; - -import { ObservedSurfaceLayer } from "./ObservedSurfaceLayer"; -import { RealizationGridLayer } from "./RealizationGridLayer"; -import { RealizationPolygonsLayer } from "./RealizationPolygonsLayer"; -import { RealizationSurfaceLayer } from "./RealizationSurfaceLayer"; -import { StatisticalSurfaceLayer } from "./StatisticalSurfaceLayer"; -import { CustomLayerType } from "./layerTypes"; - -LayerRegistry.registerLayer(CustomLayerType.OBSERVED_SURFACE, ObservedSurfaceLayer); -LayerRegistry.registerLayer(CustomLayerType.REALIZATION_GRID, RealizationGridLayer); -LayerRegistry.registerLayer(CustomLayerType.REALIZATION_POLYGONS, RealizationPolygonsLayer); -LayerRegistry.registerLayer(CustomLayerType.REALIZATION_SURFACE, RealizationSurfaceLayer); -LayerRegistry.registerLayer(CustomLayerType.STATISTICAL_SURFACE, StatisticalSurfaceLayer); diff --git a/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx index a0041cfa9..c50d3f020 100644 --- a/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx @@ -8,10 +8,6 @@ import { Menu } from "@lib/components/Menu"; import { MenuButton } from "@lib/components/MenuButton"; import { MenuHeading } from "@lib/components/MenuHeading"; import { MenuItem } from "@lib/components/MenuItem"; -import { ObservedSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer"; -import { RealizationSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; -import { StatisticalSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; -import { CustomLayerType } from "@modules/2DViewer/LayerFramework/customLayerImplementations/layerTypes"; import { PreferredViewLayout } from "@modules/2DViewer/types"; import type { LayersActionGroup } from "@modules/_shared/LayerFramework/LayersActions"; import type { GroupDelegate } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; @@ -27,6 +23,9 @@ import { GroupRegistry } from "@modules/_shared/LayerFramework/groups/GroupRegis import type { Item, ItemGroup } from "@modules/_shared/LayerFramework/interfacesAndTypes/entitites"; import { instanceofItemGroup } from "@modules/_shared/LayerFramework/interfacesAndTypes/entitites"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { ObservedSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/ObservedSurfaceLayer"; +import { RealizationSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer"; +import { StatisticalSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer"; import { LayerType } from "@modules/_shared/LayerFramework/layers/layerTypes"; import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; @@ -72,39 +71,29 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper groupDelegate.appendChild(new SharedSetting(Setting.COLOR_SCALE, null, props.layerManager)); return; case "observed-surface": - groupDelegate.prependChild( - LayerRegistry.makeLayer(CustomLayerType.OBSERVED_SURFACE, props.layerManager) - ); + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.OBSERVED_SURFACE, props.layerManager)); return; case "statistical-surface": - groupDelegate.prependChild( - LayerRegistry.makeLayer(CustomLayerType.STATISTICAL_SURFACE, props.layerManager) - ); + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.STATISTICAL_SURFACE, props.layerManager)); return; case "realization-surface": - groupDelegate.prependChild( - LayerRegistry.makeLayer(CustomLayerType.REALIZATION_SURFACE, props.layerManager) - ); + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_SURFACE, props.layerManager)); return; case "realization-polygons": - groupDelegate.prependChild( - LayerRegistry.makeLayer(CustomLayerType.REALIZATION_POLYGONS, props.layerManager) - ); + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_POLYGONS, props.layerManager)); return; case "drilled-wellbore-trajectories": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.DRILLED_WELL_TRAJECTORIES, props.layerManager) + LayerRegistry.makeLayer(LayerType.DRILLED_WELL_TRAJECTORIES, props.layerManager), ); return; case "drilled-wellbore-picks": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.DRILLED_WELLBORE_PICKS, props.layerManager) + LayerRegistry.makeLayer(LayerType.DRILLED_WELLBORE_PICKS, props.layerManager), ); return; case "realization-grid": - groupDelegate.prependChild( - LayerRegistry.makeLayer(CustomLayerType.REALIZATION_GRID, props.layerManager) - ); + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_GRID, props.layerManager)); return; case "ensemble": groupDelegate.appendChild(new SharedSetting(Setting.ENSEMBLE, null, props.layerManager)); diff --git a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx index ef2275848..fabff2fc4 100644 --- a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx @@ -7,26 +7,17 @@ import { PendingWrapper } from "@lib/components/PendingWrapper"; import { useElementSize } from "@lib/hooks/useElementSize"; import * as bbox from "@lib/utils/bbox"; import { makeColorScaleAnnotation } from "@modules/2DViewer/LayerFramework/annotations/makeColorScaleAnnotation"; -import { makePolygonDataBoundingBox } from "@modules/2DViewer/LayerFramework/boundingBoxes/makePolygonDataBoundingBox"; -import { makeRealizationGridBoundingBox } from "@modules/2DViewer/LayerFramework/boundingBoxes/makeRealizationGridBoundingBox"; -import { makeSurfaceLayerBoundingBox } from "@modules/2DViewer/LayerFramework/boundingBoxes/makeSurfaceLayerBoundingBox"; -import { ObservedSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer"; -import { RealizationGridLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer"; -import { RealizationPolygonsLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer"; -import { RealizationSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; -import { StatisticalSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; -import { CustomLayerType } from "@modules/2DViewer/LayerFramework/customLayerImplementations/layerTypes"; -import { makeObservedSurfaceLayer } from "@modules/2DViewer/LayerFramework/visualization/makeObservedSurfaceLayer"; -import { makeRealizationGridLayer } from "@modules/2DViewer/LayerFramework/visualization/makeRealizationGridLayer"; -import { makeRealizationPolygonsLayer } from "@modules/2DViewer/LayerFramework/visualization/makeRealizationPolygonsLayer"; -import { makeRealizationSurfaceLayer } from "@modules/2DViewer/LayerFramework/visualization/makeRealizationSurfaceLayer"; -import { makeStatisticalSurfaceLayer } from "@modules/2DViewer/LayerFramework/visualization/makeStatisticalSurfaceLayer"; import type { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; import type { DataLayerManager } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; import { LayerManagerTopic } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; +import { ObservedSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/ObservedSurfaceLayer"; +import { RealizationGridLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer"; +import { RealizationPolygonsLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationPolygonsLayer"; +import { RealizationSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer"; +import { StatisticalSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer"; import { LayerType } from "@modules/_shared/LayerFramework/layers/layerTypes"; import type { Annotation, @@ -36,8 +27,16 @@ import type { import { VisualizationFactory } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellTrajectoriesBoundingBox"; import { makeDrilledWellborePicksBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellborePicksBoundingBox"; +import { makePolygonDataBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makePolygonDataBoundingBox"; +import { makeRealizationGridBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationGridBoundingBox"; +import { makeSurfaceLayerBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeSurfaceLayerBoundingBox"; import { makeDrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer"; import { makeDrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellborePicksLayer"; +import { makeObservedSurfaceLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeObservedSurfaceLayer"; +import { makeRealizationGridLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer"; +import { makeRealizationPolygonsLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationPolygonsLayer"; +import { makeRealizationSurfaceLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationSurfaceLayer"; +import { makeStatisticalSurfaceLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeStatisticalSurfaceLayer"; import { ColorLegendsContainer } from "@modules/_shared/components/ColorLegendsContainer"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import type { BoundingBox2D, ViewportType } from "@webviz/subsurface-viewer"; @@ -46,7 +45,6 @@ import type { ViewsType } from "@webviz/subsurface-viewer/dist/SubsurfaceViewer" import { ReadoutWrapper } from "./ReadoutWrapper"; import { PlaceholderLayer } from "../../../_shared/customDeckGlLayers/PlaceholderLayer"; -import "../../LayerFramework/customLayerImplementations/registerAllLayers"; export type LayersWrapperProps = { layerManager: DataLayerManager; @@ -56,27 +54,27 @@ export type LayersWrapperProps = { const VISUALIZATION_FACTORY = new VisualizationFactory(); -VISUALIZATION_FACTORY.registerLayerFunctions(CustomLayerType.OBSERVED_SURFACE, ObservedSurfaceLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.OBSERVED_SURFACE, ObservedSurfaceLayer, { makeVisualizationFunction: makeObservedSurfaceLayer, calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, }); -VISUALIZATION_FACTORY.registerLayerFunctions(CustomLayerType.REALIZATION_SURFACE, RealizationSurfaceLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_SURFACE, RealizationSurfaceLayer, { makeVisualizationFunction: makeRealizationSurfaceLayer, calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, }); -VISUALIZATION_FACTORY.registerLayerFunctions(CustomLayerType.STATISTICAL_SURFACE, StatisticalSurfaceLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.STATISTICAL_SURFACE, StatisticalSurfaceLayer, { makeVisualizationFunction: makeStatisticalSurfaceLayer, calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, }); -VISUALIZATION_FACTORY.registerLayerFunctions(CustomLayerType.REALIZATION_POLYGONS, RealizationPolygonsLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_POLYGONS, RealizationPolygonsLayer, { makeVisualizationFunction: makeRealizationPolygonsLayer, calculateBoundingBoxFunction: makePolygonDataBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, }); -VISUALIZATION_FACTORY.registerLayerFunctions(CustomLayerType.REALIZATION_GRID, RealizationGridLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_GRID, RealizationGridLayer, { makeVisualizationFunction: makeRealizationGridLayer, calculateBoundingBoxFunction: makeRealizationGridBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, @@ -157,7 +155,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode {
{view.name}
- + , ); } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts deleted file mode 100644 index d12d5123e..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridLayer.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { getWellTrajectoriesOptions, postGetPolylineIntersectionOptions } from "@api"; -import { IntersectionReferenceSystem } from "@equinor/esv-intersection"; -import * as bbox from "@lib/utils/boundingBox"; -import * as vec3 from "@lib/utils/vec3"; -import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; -import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { - PolylineIntersection_trans, - calcExtendedSimplifiedWellboreTrajectoryInXYPlane, - transformPolylineIntersection, -} from "@modules/_shared/utils/wellbore"; -import { QueryClient } from "@tanstack/react-query"; - -import { isEqual } from "lodash"; - -import { IntersectionRealizationGridSettingsContext } from "./IntersectionRealizationGridSettingsContext"; -import { IntersectionRealizationGridSettings } from "./types"; - -export class IntersectionRealizationGridLayer - implements Layer -{ - private _layerDelegate: LayerDelegate; - private _itemDelegate: ItemDelegate; - - constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Intersection Realization Grid", layerManager); - this._layerDelegate = new LayerDelegate( - this, - layerManager, - new IntersectionRealizationGridSettingsContext(layerManager), - LayerColoringType.COLORSCALE - ); - } - - getSettingsContext() { - return this._layerDelegate.getSettingsContext(); - } - - getItemDelegate(): ItemDelegate { - return this._itemDelegate; - } - - getLayerDelegate(): LayerDelegate { - return this._layerDelegate; - } - - doSettingsChangesRequireDataRefetch( - prevSettings: IntersectionRealizationGridSettings, - newSettings: IntersectionRealizationGridSettings - ): boolean { - return !isEqual(prevSettings, newSettings); - } - - makeBoundingBox(): bbox.BBox | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - const boundingBox = bbox.create( - vec3.create(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY), - vec3.create(Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY) - ); - - for (const section of data.fenceMeshSections) { - boundingBox.min.x = Math.min(boundingBox.min.x, section.start_utm_x, section.end_utm_x); - boundingBox.max.x = Math.max(boundingBox.max.x, section.start_utm_x, section.end_utm_x); - boundingBox.min.y = Math.min(boundingBox.min.y, section.start_utm_y, section.end_utm_y); - boundingBox.max.y = Math.max(boundingBox.max.y, section.start_utm_y, section.end_utm_y); - for (let i = 2; i < section.verticesUzFloat32Arr.length; i += 3) { - boundingBox.min.z = Math.min(boundingBox.min.z, section.verticesUzFloat32Arr[i]); - boundingBox.max.z = Math.max(boundingBox.max.z, section.verticesUzFloat32Arr[i]); - } - } - - return boundingBox; - } - - makeValueRange(): [number, number] | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - if (data) { - return [data.min_grid_prop_value, data.max_grid_prop_value]; - } - - return null; - } - - fetchData(queryClient: QueryClient): Promise { - const settings = this.getSettingsContext().getDelegate().getSettings(); - const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); - const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); - const intersection = settings[SettingType.INTERSECTION].getDelegate().getValue(); - const gridName = settings[SettingType.GRID_NAME].getDelegate().getValue(); - const parameterName = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - let timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - if (timeOrInterval === "NO_TIME") { - timeOrInterval = null; - } - - const fieldIdentifier = this._itemDelegate.getLayerManager().getGlobalSetting("fieldId"); - - const queryKey = [ - "gridIntersection", - ensembleIdent, - gridName, - parameterName, - timeOrInterval, - realizationNum, - intersection, - ]; - this._layerDelegate.registerQueryKey(queryKey); - - let makePolylinePromise: Promise = new Promise((resolve) => { - resolve([]); - }); - - if (intersection) { - makePolylinePromise = new Promise((resolve) => { - if (intersection.type === "wellbore") { - return queryClient - .fetchQuery({ - ...getWellTrajectoriesOptions({ - query: { - field_identifier: fieldIdentifier ?? "", - wellbore_uuids: [intersection.uuid], - }, - }), - }) - .then((data) => { - const path: number[][] = []; - for (const [index, northing] of data[0].northingArr.entries()) { - const easting = data[0].eastingArr[index]; - const tvd_msl = data[0].tvdMslArr[index]; - - path.push([easting, northing, tvd_msl]); - } - const offset = data[0].tvdMslArr[0]; - - const intersectionReferenceSystem = new IntersectionReferenceSystem(path); - intersectionReferenceSystem.offset = offset; - - const polylineUtmXy: number[] = []; - polylineUtmXy.push( - ...calcExtendedSimplifiedWellboreTrajectoryInXYPlane( - path, - 0, - 5 - ).simplifiedWellboreTrajectoryXy.flat() - ); - - resolve(polylineUtmXy); - }); - } else { - const intersectionPolyline = this._itemDelegate - .getLayerManager() - .getWorkbenchSession() - .getUserCreatedItems() - .getIntersectionPolylines() - .getPolyline(intersection.uuid); - if (!intersectionPolyline) { - resolve([]); - return; - } - - const polylineUtmXy: number[] = []; - for (const point of intersectionPolyline.path) { - polylineUtmXy.push(point[0], point[1]); - } - - resolve(polylineUtmXy); - } - }); - } - - const gridIntersectionPromise = makePolylinePromise - .then((polyline_utm_xy) => - queryClient.fetchQuery({ - ...postGetPolylineIntersectionOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - grid_name: gridName ?? "", - parameter_name: parameterName ?? "", - parameter_time_or_interval_str: timeOrInterval, - realization_num: realizationNum ?? 0, - }, - body: { polyline_utm_xy }, - }), - }) - ) - .then(transformPolylineIntersection); - - return gridIntersectionPromise; - } - - serializeState(): SerializedLayer { - return this._layerDelegate.serializeState(); - } - - deserializeState(serializedState: SerializedLayer): void { - this._layerDelegate.deserializeState(serializedState); - } -} - -LayerRegistry.registerLayer(IntersectionRealizationGridLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts deleted file mode 100644 index 07ef7163a..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/IntersectionRealizationGridSettingsContext.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { getDrilledWellboreHeadersOptions, getGridModelsInfoOptions } from "@api"; -import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; -import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; -import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; -import { - IntersectionSetting, - IntersectionSettingValue, -} from "@modules/_shared/LayerFramework/settings/implementations/IntersectionSetting"; -import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; -import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -import { IntersectionRealizationGridSettings } from "./types"; - -export class IntersectionRealizationGridSettingsContext - implements SettingsContext -{ - private _contextDelegate: SettingsContextDelegate; - - constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate(this, layerManager, { - [SettingType.INTERSECTION]: new IntersectionSetting(), - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.GRID_NAME]: new GridNameSetting(), - [SettingType.ATTRIBUTE]: new AttributeSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - [SettingType.SHOW_GRID_LINES]: new ShowGridLinesSetting(), - }); - } - - areCurrentSettingsValid(settings: IntersectionRealizationGridSettings): boolean { - return ( - settings[SettingType.INTERSECTION] !== null && - settings[SettingType.ENSEMBLE] !== null && - settings[SettingType.REALIZATION] !== null && - settings[SettingType.GRID_NAME] !== null && - settings[SettingType.ATTRIBUTE] !== null && - settings[SettingType.TIME_OR_INTERVAL] !== null - ); - } - - getDelegate(): SettingsContextDelegate { - return this._contextDelegate; - } - - getSettings() { - return this._contextDelegate.getSettings(); - } - - defineDependencies({ - helperDependency, - availableSettingsUpdater, - queryClient, - workbenchSession, - }: DefineDependenciesArgs) { - availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { - const fieldIdentifier = getGlobalSetting("fieldId"); - const ensembles = getGlobalSetting("ensembles"); - - const ensembleIdents = ensembles - .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) - .map((ensemble) => ensemble.getIdent()); - - return ensembleIdents; - }); - - availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); - - if (!ensembleIdent) { - return []; - } - - const realizations = realizationFilterFunc(ensembleIdent); - - return [...realizations]; - }); - - const realizationGridDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realization = getLocalSetting(SettingType.REALIZATION); - - if (!ensembleIdent || realization === null) { - return null; - } - - return await queryClient.fetchQuery({ - ...getGridModelsInfoOptions({ - query: { - case_uuid: ensembleIdent.getCaseUuid(), - ensemble_name: ensembleIdent.getEnsembleName(), - realization_num: realization, - }, - signal: abortSignal, - }), - }); - }); - - availableSettingsUpdater(SettingType.GRID_NAME, ({ getHelperDependency }) => { - const data = getHelperDependency(realizationGridDataDep); - - if (!data) { - return []; - } - - const availableGridNames = [...Array.from(new Set(data.map((gridModelInfo) => gridModelInfo.grid_name)))]; - - return availableGridNames; - }); - - availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getLocalSetting, getHelperDependency }) => { - const gridName = getLocalSetting(SettingType.GRID_NAME); - const data = getHelperDependency(realizationGridDataDep); - - if (!gridName || !data) { - return []; - } - - const gridAttributeArr = - data.find((gridModel) => gridModel.grid_name === gridName)?.property_info_arr ?? []; - - const availableGridAttributes = [ - ...Array.from(new Set(gridAttributeArr.map((gridAttribute) => gridAttribute.property_name))), - ]; - - return availableGridAttributes; - }); - - const wellboreHeadersDep = helperDependency(async function fetchData({ getLocalSetting, abortSignal }) { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - - if (!ensembleIdent) { - return null; - } - - const ensembleSet = workbenchSession.getEnsembleSet(); - const ensemble = ensembleSet.findEnsemble(ensembleIdent); - - if (!ensemble) { - return null; - } - - const fieldIdentifier = ensemble.getFieldIdentifier(); - - return await queryClient.fetchQuery({ - ...getDrilledWellboreHeadersOptions({ - query: { field_identifier: fieldIdentifier }, - signal: abortSignal, - }), - }); - }); - - availableSettingsUpdater(SettingType.INTERSECTION, ({ getHelperDependency, getGlobalSetting }) => { - const wellboreHeaders = getHelperDependency(wellboreHeadersDep); - const intersectionPolylines = getGlobalSetting("intersectionPolylines"); - - if (!wellboreHeaders) { - return []; - } - - const intersectionOptions: IntersectionSettingValue[] = []; - for (const wellboreHeader of wellboreHeaders) { - intersectionOptions.push({ - type: "wellbore", - name: wellboreHeader.uniqueWellboreIdentifier, - uuid: wellboreHeader.wellboreUuid, - }); - } - - for (const polyline of intersectionPolylines) { - intersectionOptions.push({ - type: "polyline", - name: polyline.name, - uuid: polyline.id, - }); - } - - return intersectionOptions; - }); - - availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const gridName = getLocalSetting(SettingType.GRID_NAME); - const gridAttribute = getLocalSetting(SettingType.ATTRIBUTE); - const data = getHelperDependency(realizationGridDataDep); - - if (!gridName || !gridAttribute || !data) { - return []; - } - - const gridAttributeArr = - data.find((gridModel) => gridModel.grid_name === gridName)?.property_info_arr ?? []; - - const availableTimeOrIntervals = [ - ...Array.from( - new Set( - gridAttributeArr - .filter((attr) => attr.property_name === gridAttribute) - .map((gridAttribute) => gridAttribute.iso_date_or_interval ?? "NO_TIME") - ) - ), - ]; - - return availableTimeOrIntervals; - }); - } -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/index.ts deleted file mode 100644 index 037b38884..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { IntersectionRealizationGridLayer } from "./IntersectionRealizationGridLayer"; -export { IntersectionRealizationGridSettingsContext } from "./IntersectionRealizationGridSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/types.ts deleted file mode 100644 index 3848bedff..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; -import { IntersectionSettingValue } from "@modules/_shared/LayerFramework/settings/implementations/IntersectionSetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -export type IntersectionRealizationGridSettings = { - [SettingType.INTERSECTION]: IntersectionSettingValue | null; - [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; - [SettingType.REALIZATION]: number | null; - [SettingType.ATTRIBUTE]: string | null; - [SettingType.GRID_NAME]: string | null; - [SettingType.TIME_OR_INTERVAL]: string | null; - [SettingType.SHOW_GRID_LINES]: boolean; -}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts deleted file mode 100644 index 88ad07d97..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridLayer.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { getGridParameterOptions, getGridSurfaceOptions } from "@api"; -import * as bbox from "@lib/utils/boundingBox"; -import * as vec3 from "@lib/utils/vec3"; -import { - GridMappedProperty_trans, - GridSurface_trans, - transformGridMappedProperty, - transformGridSurface, -} from "@modules/3DViewer/view/queries/queryDataTransforms"; -import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; -import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { QueryClient } from "@tanstack/react-query"; - -import { isEqual } from "lodash"; - -import { RealizationGridSettingsContext } from "./RealizationGridSettingsContext"; -import { RealizationGridSettings } from "./types"; - -export type RealizationGridLayerData = { - gridSurfaceData: GridSurface_trans; - gridParameterData: GridMappedProperty_trans; -}; - -export class RealizationGridLayer implements Layer { - private _layerDelegate: LayerDelegate; - private _itemDelegate: ItemDelegate; - - constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Realization Grid", layerManager); - this._layerDelegate = new LayerDelegate( - this, - layerManager, - new RealizationGridSettingsContext(layerManager), - LayerColoringType.COLORSCALE - ); - } - - getSettingsContext() { - return this._layerDelegate.getSettingsContext(); - } - - getItemDelegate(): ItemDelegate { - return this._itemDelegate; - } - - getLayerDelegate(): LayerDelegate { - return this._layerDelegate; - } - - doSettingsChangesRequireDataRefetch( - prevSettings: RealizationGridSettings, - newSettings: RealizationGridSettings - ): boolean { - return !isEqual(prevSettings, newSettings); - } - - makeBoundingBox(): bbox.BBox | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - if (!data.gridSurfaceData) { - return null; - } - - return bbox.create( - vec3.create(data.gridSurfaceData.xmin, data.gridSurfaceData.ymin, data.gridSurfaceData.zmin), - vec3.create(data.gridSurfaceData.xmax, data.gridSurfaceData.ymax, data.gridSurfaceData.zmax) - ); - } - - makeValueRange(): [number, number] | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - if (data.gridParameterData) { - return [data.gridParameterData.min_grid_prop_value, data.gridParameterData.max_grid_prop_value]; - } - - return null; - } - - fetchData(queryClient: QueryClient): Promise { - const settings = this.getSettingsContext().getDelegate().getSettings(); - const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); - const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); - const gridName = settings[SettingType.GRID_NAME].getDelegate().getValue(); - const parameterName = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - let parameterTimeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - if (parameterTimeOrInterval === "NO_TIME") { - parameterTimeOrInterval = null; - } - let availableDimensions = settings[SettingType.GRID_LAYER_K_RANGE].getDelegate().getAvailableValues(); - if (!availableDimensions.length || availableDimensions[0] === null) { - availableDimensions = [0, 0, 0]; - } - const layerIRange = settings[SettingType.GRID_LAYER_I_RANGE].getDelegate().getValue(); - const iMin = layerIRange?.[0] ?? 0; - const iMax = layerIRange?.[1] ?? 0; - - const layerJRange = settings[SettingType.GRID_LAYER_J_RANGE].getDelegate().getValue(); - const jMin = layerJRange?.[0] ?? 0; - const jMax = layerJRange?.[1] ?? 0; - - const layerKRange = settings[SettingType.GRID_LAYER_K_RANGE].getDelegate().getValue(); - const kMin = layerKRange?.[0] ?? 0; - const kMax = layerKRange?.[1] ?? 0; - - const queryKey = [ - "gridParameter", - ensembleIdent, - gridName, - parameterName, - parameterTimeOrInterval, - realizationNum, - iMin, - iMax, - jMin, - jMax, - kMin, - kMax, - ]; - this._layerDelegate.registerQueryKey(queryKey); - - const gridParameterPromise = queryClient - .fetchQuery({ - ...getGridParameterOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - grid_name: gridName ?? "", - parameter_name: parameterName ?? "", - parameter_time_or_interval_str: parameterTimeOrInterval, - realization_num: realizationNum ?? 0, - i_min: iMin, - i_max: iMax - 1, - j_min: jMin, - j_max: jMax - 1, - k_min: kMin, - k_max: kMax, - }, - }), - }) - .then(transformGridMappedProperty); - - const gridSurfacePromise = queryClient - .fetchQuery({ - ...getGridSurfaceOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - grid_name: gridName ?? "", - realization_num: realizationNum ?? 0, - i_min: iMin, - i_max: iMax - 1, - j_min: jMin, - j_max: jMax - 1, - k_min: kMin, - k_max: kMax, - }, - }), - }) - .then(transformGridSurface); - - return Promise.all([gridSurfacePromise, gridParameterPromise]).then(([gridSurfaceData, gridParameterData]) => ({ - gridSurfaceData, - gridParameterData, - })); - } - - serializeState(): SerializedLayer { - return this._layerDelegate.serializeState(); - } - - deserializeState(serializedState: SerializedLayer): void { - this._layerDelegate.deserializeState(serializedState); - } -} - -LayerRegistry.registerLayer(RealizationGridLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts deleted file mode 100644 index b623a5835..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/RealizationGridSettingsContext.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { getGridModelsInfoOptions } from "@api"; -import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; -import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; -import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { GridLayerIRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting"; -import { GridLayerJRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting"; -import { GridLayerKRangeSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting"; -import { GridNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/GridNameSetting"; -import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { ShowGridLinesSetting } from "@modules/_shared/LayerFramework/settings/implementations/ShowGridLinesSetting"; -import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -import { RealizationGridSettings } from "./types"; - -export class RealizationGridSettingsContext implements SettingsContext { - private _contextDelegate: SettingsContextDelegate; - - constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate(this, layerManager, { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.GRID_NAME]: new GridNameSetting(), - [SettingType.ATTRIBUTE]: new AttributeSetting(), - [SettingType.GRID_LAYER_I_RANGE]: new GridLayerIRangeSetting(), - [SettingType.GRID_LAYER_J_RANGE]: new GridLayerJRangeSetting(), - [SettingType.GRID_LAYER_K_RANGE]: new GridLayerKRangeSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - [SettingType.SHOW_GRID_LINES]: new ShowGridLinesSetting(), - }); - } - - areCurrentSettingsValid(settings: RealizationGridSettings): boolean { - return ( - settings[SettingType.ENSEMBLE] !== null && - settings[SettingType.REALIZATION] !== null && - settings[SettingType.GRID_NAME] !== null && - settings[SettingType.ATTRIBUTE] !== null && - settings[SettingType.GRID_LAYER_I_RANGE] !== null && - settings[SettingType.GRID_LAYER_J_RANGE] !== null && - settings[SettingType.GRID_LAYER_K_RANGE] !== null && - settings[SettingType.TIME_OR_INTERVAL] !== null - ); - } - - getDelegate(): SettingsContextDelegate { - return this._contextDelegate; - } - - getSettings() { - return this._contextDelegate.getSettings(); - } - - defineDependencies({ - helperDependency, - availableSettingsUpdater, - queryClient, - workbenchSession, - }: DefineDependenciesArgs) { - availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { - const fieldIdentifier = getGlobalSetting("fieldId"); - const ensembles = getGlobalSetting("ensembles"); - - const ensembleIdents = ensembles - .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) - .map((ensemble) => ensemble.getIdent()); - - return ensembleIdents; - }); - - availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); - - if (!ensembleIdent) { - return []; - } - - const realizations = realizationFilterFunc(ensembleIdent); - - return [...realizations]; - }); - - const realizationGridDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realization = getLocalSetting(SettingType.REALIZATION); - - if (!ensembleIdent || realization === null) { - return null; - } - - return await queryClient.fetchQuery({ - ...getGridModelsInfoOptions({ - query: { - case_uuid: ensembleIdent.getCaseUuid(), - ensemble_name: ensembleIdent.getEnsembleName(), - realization_num: realization, - }, - signal: abortSignal, - }), - }); - }); - - availableSettingsUpdater(SettingType.GRID_NAME, ({ getHelperDependency }) => { - const data = getHelperDependency(realizationGridDataDep); - - if (!data) { - return []; - } - - const availableGridNames = [...Array.from(new Set(data.map((gridModelInfo) => gridModelInfo.grid_name)))]; - - return availableGridNames; - }); - - availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getLocalSetting, getHelperDependency }) => { - const gridName = getLocalSetting(SettingType.GRID_NAME); - const data = getHelperDependency(realizationGridDataDep); - - if (!gridName || !data) { - return []; - } - - const gridAttributeArr = - data.find((gridModel) => gridModel.grid_name === gridName)?.property_info_arr ?? []; - - const availableGridAttributes = [ - ...Array.from(new Set(gridAttributeArr.map((gridAttribute) => gridAttribute.property_name))), - ]; - - return availableGridAttributes; - }); - - availableSettingsUpdater(SettingType.GRID_LAYER_I_RANGE, ({ getLocalSetting, getHelperDependency }) => { - const gridName = getLocalSetting(SettingType.GRID_NAME); - const data = getHelperDependency(realizationGridDataDep); - - if (!gridName || !data) { - return []; - } - - const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; - const availableGridLayers: number[] = []; - if (gridDimensions) { - availableGridLayers.push(gridDimensions.i_count); - availableGridLayers.push(gridDimensions.j_count); - availableGridLayers.push(gridDimensions.k_count); - } - - return availableGridLayers; - }); - - availableSettingsUpdater(SettingType.GRID_LAYER_J_RANGE, ({ getLocalSetting, getHelperDependency }) => { - const gridName = getLocalSetting(SettingType.GRID_NAME); - const data = getHelperDependency(realizationGridDataDep); - - if (!gridName || !data) { - return []; - } - - const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; - const availableGridLayers: number[] = []; - if (gridDimensions) { - availableGridLayers.push(gridDimensions.i_count); - availableGridLayers.push(gridDimensions.j_count); - availableGridLayers.push(gridDimensions.k_count); - } - - return availableGridLayers; - }); - - availableSettingsUpdater(SettingType.GRID_LAYER_K_RANGE, ({ getLocalSetting, getHelperDependency }) => { - const gridName = getLocalSetting(SettingType.GRID_NAME); - const data = getHelperDependency(realizationGridDataDep); - - if (!gridName || !data) { - return []; - } - - const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; - const availableGridLayers: number[] = []; - if (gridDimensions) { - availableGridLayers.push(gridDimensions.i_count); - availableGridLayers.push(gridDimensions.j_count); - availableGridLayers.push(gridDimensions.k_count); - } - - return availableGridLayers; - }); - - availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const gridName = getLocalSetting(SettingType.GRID_NAME); - const gridAttribute = getLocalSetting(SettingType.ATTRIBUTE); - const data = getHelperDependency(realizationGridDataDep); - - if (!gridName || !gridAttribute || !data) { - return []; - } - - const gridAttributeArr = - data.find((gridModel) => gridModel.grid_name === gridName)?.property_info_arr ?? []; - - const availableTimeOrIntervals = [ - ...Array.from( - new Set( - gridAttributeArr - .filter((attr) => attr.property_name === gridAttribute) - .map((gridAttribute) => gridAttribute.iso_date_or_interval ?? "NO_TIME") - ) - ), - ]; - - return availableTimeOrIntervals; - }); - } -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/index.ts deleted file mode 100644 index 377b23ffe..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { RealizationGridLayer } from "./RealizationGridLayer"; -export { RealizationGridSettingsContext } from "./RealizationGridSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts deleted file mode 100644 index da1eb103d..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -export type RealizationGridSettings = { - [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; - [SettingType.REALIZATION]: number | null; - [SettingType.ATTRIBUTE]: string | null; - [SettingType.GRID_NAME]: string | null; - [SettingType.GRID_LAYER_I_RANGE]: [number, number] | null; - [SettingType.GRID_LAYER_J_RANGE]: [number, number] | null; - [SettingType.GRID_LAYER_K_RANGE]: [number, number] | null; - [SettingType.TIME_OR_INTERVAL]: string | null; - [SettingType.SHOW_GRID_LINES]: boolean; -}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer.ts new file mode 100644 index 000000000..9a1ff9343 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer.ts @@ -0,0 +1,214 @@ +import { type SeismicCubeMeta_api, getCrosslineSliceOptions, getSeismicCubeMetaListOptions } from "@api"; +import { + type SeismicSliceData_trans, + transformSeismicSlice, +} from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; +import type { + CustomDataLayerImplementation, + DataLayerInformationAccessors, + FetchDataParams, +} from "@modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation"; +import type { DefineDependenciesArgs } from "@modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler"; +import { type MakeSettingTypesMap, Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; + +import { isEqual } from "lodash"; + +const realizationSeismicCrosslineSettings = [ + Setting.ENSEMBLE, + Setting.REALIZATION, + Setting.ATTRIBUTE, + Setting.TIME_OR_INTERVAL, + Setting.SEISMIC_CROSSLINE, + Setting.COLOR_SCALE, +] as const; +export type RealizationSeismicCrosslineSettings = typeof realizationSeismicCrosslineSettings; +type SettingsWithTypes = MakeSettingTypesMap; + +export type RealizationSeismicCrosslineData = SeismicSliceData_trans; + +export type RealizationSeismicCrosslineStoredData = { + seismicCubeMeta: SeismicCubeMeta_api[]; +}; + +export class RealizationSeismicCrosslineLayer + implements + CustomDataLayerImplementation< + RealizationSeismicCrosslineSettings, + RealizationSeismicCrosslineData, + RealizationSeismicCrosslineStoredData + > +{ + settings = realizationSeismicCrosslineSettings; + + getDefaultName(): string { + return "Seismic Crossline (realization)"; + } + + doSettingsChangesRequireDataRefetch(prevSettings: SettingsWithTypes, newSettings: SettingsWithTypes): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeValueRange( + accessors: DataLayerInformationAccessors< + RealizationSeismicCrosslineSettings, + RealizationSeismicCrosslineData, + RealizationSeismicCrosslineStoredData + >, + ): [number, number] | null { + const data = accessors.getData(); + if (!data) { + return null; + } + return [data.value_min, data.value_max]; + } + + fetchData({ + getSetting, + registerQueryKey, + queryClient, + }: FetchDataParams< + RealizationSeismicCrosslineSettings, + RealizationSeismicCrosslineData + >): Promise { + const ensembleIdent = getSetting(Setting.ENSEMBLE); + const realizationNum = getSetting(Setting.REALIZATION); + const attribute = getSetting(Setting.ATTRIBUTE); + const timeOrInterval = getSetting(Setting.TIME_OR_INTERVAL); + const crosslineNumber = getSetting(Setting.SEISMIC_CROSSLINE); + + const queryOptions = getCrosslineSliceOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + realization_num: realizationNum ?? 0, + seismic_attribute: attribute ?? "", + time_or_interval_str: timeOrInterval ?? "", + observed: false, + crossline_no: crosslineNumber ?? 0, + }, + }); + + registerQueryKey(queryOptions.queryKey); + + return queryClient + .fetchQuery({ + ...queryOptions, + }) + .then((data) => transformSeismicSlice(data)); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + storedDataUpdater, + queryClient, + }: DefineDependenciesArgs): void { + availableSettingsUpdater(Setting.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + availableSettingsUpdater(Setting.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { + const ensembleIdent = getLocalSetting(Setting.ENSEMBLE); + const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); + + if (!ensembleIdent) { + return []; + } + + const realizations = realizationFilterFunc(ensembleIdent); + + return [...realizations]; + }); + + const realizationSeismicCrosslineDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(Setting.ENSEMBLE); + const realization = getLocalSetting(Setting.REALIZATION); + + if (!ensembleIdent || realization === null) { + return null; + } + + return await queryClient.fetchQuery({ + ...getSeismicCubeMetaListOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + }, + signal: abortSignal, + }), + }); + }); + + storedDataUpdater("seismicCubeMeta", ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!data) { + return null; + } + + return data; + }); + + availableSettingsUpdater(Setting.ATTRIBUTE, ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!data) { + return []; + } + + const availableSeismicAttributes = [ + ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismicAttribute))), + ]; + + return availableSeismicAttributes; + }); + + availableSettingsUpdater(Setting.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(Setting.ATTRIBUTE); + + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!seismicAttribute || !data) { + return []; + } + + const availableTimeOrIntervals = [ + ...Array.from( + new Set( + data + .filter((surface) => surface.seismicAttribute === seismicAttribute) + .map((el) => el.isoDateOrInterval), + ), + ), + ]; + + return availableTimeOrIntervals; + }); + + availableSettingsUpdater(Setting.SEISMIC_CROSSLINE, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(Setting.ATTRIBUTE); + const timeOrInterval = getLocalSetting(Setting.TIME_OR_INTERVAL); + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!seismicAttribute || !timeOrInterval || !data) { + return [0, 0, 0]; + } + const seismicInfo = data.filter( + (seismicInfos) => + seismicInfos.seismicAttribute === seismicAttribute && + seismicInfos.isoDateOrInterval === timeOrInterval, + )[0]; + const jMin = 0; + const jMax = seismicInfo.spec.numRows - 1; + + return [jMin, jMax, 1]; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts deleted file mode 100644 index ca66f7fe4..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineLayer.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { getCrosslineSliceOptions } from "@api"; -import * as bbox from "@lib/utils/boundingBox"; -import { Geometry, ShapeType } from "@lib/utils/geometry"; -import { rotatePoint2Around } from "@lib/utils/vec2"; -import * as vec3 from "@lib/utils/vec3"; -import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; -import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { QueryClient } from "@tanstack/react-query"; - -import { isEqual } from "lodash"; - -import { RealizationSeismicCrosslineSettingsContext } from "./RealizationSeismicCrosslineSettingsContext"; -import { RealizationSeismicCrosslineSettings, RealizationSeismicCrosslineStoredData } from "./types"; - -import { SeismicSliceData_trans, transformSeismicSlice } from "../../../settings/queries/queryDataTransforms"; - -export class RealizationSeismicCrosslineLayer - implements - Layer -{ - private _layerDelegate: LayerDelegate< - RealizationSeismicCrosslineSettings, - SeismicSliceData_trans, - RealizationSeismicCrosslineStoredData - >; - private _itemDelegate: ItemDelegate; - - constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Seismic Crossline (realization)", layerManager); - this._layerDelegate = new LayerDelegate( - this, - layerManager, - new RealizationSeismicCrosslineSettingsContext(layerManager), - LayerColoringType.COLORSCALE - ); - } - - getSettingsContext() { - return this._layerDelegate.getSettingsContext(); - } - - getItemDelegate(): ItemDelegate { - return this._itemDelegate; - } - - getLayerDelegate() { - return this._layerDelegate; - } - - doSettingsChangesRequireDataRefetch( - prevSettings: RealizationSeismicCrosslineSettings, - newSettings: RealizationSeismicCrosslineSettings - ): boolean { - return !isEqual(prevSettings, newSettings); - } - - makeBoundingBox(): bbox.BBox | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return bbox.create( - vec3.create(data.bbox_utm[0][0], data.bbox_utm[0][1], data.u_min), - vec3.create(data.bbox_utm[1][0], data.bbox_utm[1][1], data.u_max) - ); - } - - predictNextGeometry(): Geometry | null { - const settings = this.getSettingsContext().getDelegate().getSettings(); - const seismicCrosslineNumber = settings[SettingType.SEISMIC_CROSSLINE].getDelegate().getValue(); - const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - const isoTimeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - const seismicCubeMeta = this._layerDelegate.getSettingsContext().getDelegate().getStoredData("seismicCubeMeta"); - - if (!seismicCubeMeta || seismicCrosslineNumber === null) { - return null; - } - - const meta = seismicCubeMeta.find( - (m) => m.seismicAttribute === seismicAttribute && m.isoDateOrInterval === isoTimeOrInterval - ); - - if (!meta) { - return null; - } - - const xmin = meta.spec.xOrigin; - const xmax = meta.spec.xOrigin + meta.spec.xInc * (meta.spec.numCols - 1); - - const ymin = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * seismicCrosslineNumber; - const ymax = ymin; - - const zmin = meta.spec.zOrigin; - const zmax = meta.spec.zOrigin + meta.spec.zInc * meta.spec.zFlip * (meta.spec.numLayers - 1); - - const maxXY = { x: xmax, y: ymax }; - const minXY = { x: xmin, y: ymin }; - const origin = { x: meta.spec.xOrigin, y: meta.spec.yOrigin }; - - const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); - const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); - - const geometry: Geometry = { - shapes: [ - { - type: ShapeType.RECTANGLE, - centerPoint: vec3.create( - (rotatedMinXY.x + rotatedMaxXY.x) / 2, - (rotatedMinXY.y + rotatedMaxXY.y) / 2, - (zmin + zmax) / 2 - ), - dimensions: { - width: vec3.length( - vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0) - ), - height: Math.abs(zmax - zmin), - }, - normalizedEdgeVectors: { - u: vec3.normalize( - vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0) - ), - v: vec3.create(0, 0, 1), - }, - }, - ], - boundingBox: bbox.create( - vec3.create(Math.min(rotatedMinXY.x, rotatedMaxXY.x), Math.min(rotatedMinXY.y, rotatedMaxXY.y), zmin), - vec3.create(Math.max(rotatedMinXY.x, rotatedMaxXY.x), Math.max(rotatedMinXY.y, rotatedMaxXY.y), zmax) - ), - }; - - return geometry; - } - - makeValueRange(): [number, number] | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return [data.value_min, data.value_max]; - } - - fetchData(queryClient: QueryClient): Promise { - const settings = this.getSettingsContext().getDelegate().getSettings(); - const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); - const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); - const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - - const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - const seismicCrosslineNumber = settings[SettingType.SEISMIC_CROSSLINE].getDelegate().getValue(); - - const queryOptions = getCrosslineSliceOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - realization_num: realizationNum ?? 0, - seismic_attribute: seismicAttribute ?? "", - time_or_interval_str: timeOrInterval ?? "", - observed: false, - crossline_no: seismicCrosslineNumber ?? 0, - }, - }); - - this._layerDelegate.registerQueryKey(queryOptions.queryKey); - - const seismicSlicePromise = queryClient - .fetchQuery({ - ...queryOptions, - }) - .then((data) => transformSeismicSlice(data)); - - return seismicSlicePromise; - } - - serializeState(): SerializedLayer { - return this._layerDelegate.serializeState(); - } - - deserializeState(serializedState: SerializedLayer): void { - this._layerDelegate.deserializeState(serializedState); - } -} - -LayerRegistry.registerLayer(RealizationSeismicCrosslineLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts deleted file mode 100644 index 9b643571d..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/RealizationSeismicCrosslineSettingsContext.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { getSeismicCubeMetaListOptions } from "@api"; -import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; -import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; -import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SeismicCrosslineSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting"; -import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -import { RealizationSeismicCrosslineSettings, RealizationSeismicCrosslineStoredData } from "./types"; - -export class RealizationSeismicCrosslineSettingsContext - implements SettingsContext -{ - private _contextDelegate: SettingsContextDelegate< - RealizationSeismicCrosslineSettings, - RealizationSeismicCrosslineStoredData - >; - - constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - RealizationSeismicCrosslineSettings, - RealizationSeismicCrosslineStoredData - >(this, layerManager, { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.ATTRIBUTE]: new AttributeSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - [SettingType.SEISMIC_CROSSLINE]: new SeismicCrosslineSetting(), - }); - } - - areCurrentSettingsValid(settings: RealizationSeismicCrosslineSettings): boolean { - return ( - settings[SettingType.ENSEMBLE] !== null && - settings[SettingType.REALIZATION] !== null && - settings[SettingType.ATTRIBUTE] !== null && - settings[SettingType.TIME_OR_INTERVAL] !== null - ); - } - - getDelegate() { - return this._contextDelegate; - } - - getSettings() { - return this._contextDelegate.getSettings(); - } - - defineDependencies({ - helperDependency, - availableSettingsUpdater, - storedDataUpdater, - queryClient, - }: DefineDependenciesArgs) { - availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { - const fieldIdentifier = getGlobalSetting("fieldId"); - const ensembles = getGlobalSetting("ensembles"); - - const ensembleIdents = ensembles - .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) - .map((ensemble) => ensemble.getIdent()); - - return ensembleIdents; - }); - - availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); - - if (!ensembleIdent) { - return []; - } - - const realizations = realizationFilterFunc(ensembleIdent); - - return [...realizations]; - }); - - const realizationSeismicCrosslineDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realization = getLocalSetting(SettingType.REALIZATION); - - if (!ensembleIdent || realization === null) { - return null; - } - - return await queryClient.fetchQuery({ - ...getSeismicCubeMetaListOptions({ - query: { - case_uuid: ensembleIdent.getCaseUuid(), - ensemble_name: ensembleIdent.getEnsembleName(), - }, - signal: abortSignal, - }), - }); - }); - - storedDataUpdater("seismicCubeMeta", ({ getHelperDependency }) => { - const data = getHelperDependency(realizationSeismicCrosslineDataDep); - - if (!data) { - return null; - } - - return data; - }); - - availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { - const data = getHelperDependency(realizationSeismicCrosslineDataDep); - - if (!data) { - return []; - } - - const availableSeismicAttributes = [ - ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismicAttribute))), - ]; - - return availableSeismicAttributes; - }); - - availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); - - const data = getHelperDependency(realizationSeismicCrosslineDataDep); - - if (!seismicAttribute || !data) { - return []; - } - - const availableTimeOrIntervals = [ - ...Array.from( - new Set( - data - .filter((surface) => surface.seismicAttribute === seismicAttribute) - .map((el) => el.isoDateOrInterval) - ) - ), - ]; - - return availableTimeOrIntervals; - }); - - availableSettingsUpdater(SettingType.SEISMIC_CROSSLINE, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); - const timeOrInterval = getLocalSetting(SettingType.TIME_OR_INTERVAL); - const data = getHelperDependency(realizationSeismicCrosslineDataDep); - - if (!seismicAttribute || !timeOrInterval || !data) { - return []; - } - const seismicInfo = data.filter( - (seismicInfos) => - seismicInfos.seismicAttribute === seismicAttribute && - seismicInfos.isoDateOrInterval === timeOrInterval - )[0]; - const jMin = 0; - const jMax = seismicInfo.spec.numRows - 1; - - return [jMin, jMax]; - }); - } -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/index.ts deleted file mode 100644 index 67f5bab9f..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { RealizationSeismicCrosslineLayer } from "./RealizationSeismicCrosslineLayer"; -export { RealizationSeismicCrosslineSettingsContext } from "./RealizationSeismicCrosslineSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts deleted file mode 100644 index d23a36771..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { SeismicCubeMeta_api } from "@api"; -import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -export type RealizationSeismicCrosslineSettings = { - [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; - [SettingType.REALIZATION]: number | null; - [SettingType.ATTRIBUTE]: string | null; - [SettingType.TIME_OR_INTERVAL]: string | null; - [SettingType.SEISMIC_CROSSLINE]: number | null; -}; - -export type RealizationSeismicCrosslineStoredData = { - seismicCubeMeta: SeismicCubeMeta_api[]; -}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts new file mode 100644 index 000000000..2996fdc43 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts @@ -0,0 +1,217 @@ +import { type SeismicCubeMeta_api, getInlineSliceOptions, getSeismicCubeMetaListOptions } from "@api"; +import { + type SeismicSliceData_trans, + transformSeismicSlice, +} from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; +import type { + CustomDataLayerImplementation, + DataLayerInformationAccessors, + FetchDataParams, +} from "@modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation"; +import type { DefineDependenciesArgs } from "@modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler"; +import { type MakeSettingTypesMap, Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; + +import { isEqual } from "lodash"; + +const realizationSeismicDepthSliceSettings = [ + Setting.ENSEMBLE, + Setting.REALIZATION, + Setting.ATTRIBUTE, + Setting.TIME_OR_INTERVAL, + Setting.SEISMIC_DEPTH_SLICE, + Setting.COLOR_SCALE, +] as const; +export type RealizationSeismicDepthSliceSettings = typeof realizationSeismicDepthSliceSettings; +type SettingsWithTypes = MakeSettingTypesMap; + +export type RealizationSeismicDepthSliceData = SeismicSliceData_trans; + +export type RealizationSeismicDepthSliceStoredData = { + seismicCubeMeta: SeismicCubeMeta_api[]; +}; + +export class RealizationSeismicDepthSliceLayer + implements + CustomDataLayerImplementation< + RealizationSeismicDepthSliceSettings, + RealizationSeismicDepthSliceData, + RealizationSeismicDepthSliceStoredData + > +{ + settings = realizationSeismicDepthSliceSettings; + + getDefaultName(): string { + return "Seismic Inline (realization)"; + } + + doSettingsChangesRequireDataRefetch(prevSettings: SettingsWithTypes, newSettings: SettingsWithTypes): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeValueRange( + accessors: DataLayerInformationAccessors< + RealizationSeismicDepthSliceSettings, + RealizationSeismicDepthSliceData, + RealizationSeismicDepthSliceStoredData + >, + ): [number, number] | null { + const data = accessors.getData(); + if (!data) { + return null; + } + return [data.value_min, data.value_max]; + } + + fetchData({ + getSetting, + registerQueryKey, + queryClient, + }: FetchDataParams< + RealizationSeismicDepthSliceSettings, + RealizationSeismicDepthSliceData + >): Promise { + const ensembleIdent = getSetting(Setting.ENSEMBLE); + const realizationNum = getSetting(Setting.REALIZATION); + const attribute = getSetting(Setting.ATTRIBUTE); + const timeOrInterval = getSetting(Setting.TIME_OR_INTERVAL); + const depthSlice = getSetting(Setting.SEISMIC_DEPTH_SLICE); + + const queryOptions = getInlineSliceOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + realization_num: realizationNum ?? 0, + seismic_attribute: attribute ?? "", + time_or_interval_str: timeOrInterval ?? "", + observed: false, + inline_no: depthSlice ?? 0, + }, + }); + + registerQueryKey(queryOptions.queryKey); + + return queryClient + .fetchQuery({ + ...queryOptions, + }) + .then((data) => transformSeismicSlice(data)); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + storedDataUpdater, + queryClient, + }: DefineDependenciesArgs): void { + availableSettingsUpdater(Setting.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + availableSettingsUpdater(Setting.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { + const ensembleIdent = getLocalSetting(Setting.ENSEMBLE); + const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); + + if (!ensembleIdent) { + return []; + } + + const realizations = realizationFilterFunc(ensembleIdent); + + return [...realizations]; + }); + + const realizationSeismicCrosslineDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(Setting.ENSEMBLE); + const realization = getLocalSetting(Setting.REALIZATION); + + if (!ensembleIdent || realization === null) { + return null; + } + + return await queryClient.fetchQuery({ + ...getSeismicCubeMetaListOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + }, + signal: abortSignal, + }), + }); + }); + + storedDataUpdater("seismicCubeMeta", ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!data) { + return null; + } + + return data; + }); + + availableSettingsUpdater(Setting.ATTRIBUTE, ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!data) { + return []; + } + + const availableSeismicAttributes = [ + ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismicAttribute))), + ]; + + return availableSeismicAttributes; + }); + + availableSettingsUpdater(Setting.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(Setting.ATTRIBUTE); + + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!seismicAttribute || !data) { + return []; + } + + const availableTimeOrIntervals = [ + ...Array.from( + new Set( + data + .filter((surface) => surface.seismicAttribute === seismicAttribute) + .map((el) => el.isoDateOrInterval), + ), + ), + ]; + + return availableTimeOrIntervals; + }); + + availableSettingsUpdater(Setting.SEISMIC_DEPTH_SLICE, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(Setting.ATTRIBUTE); + const timeOrInterval = getLocalSetting(Setting.TIME_OR_INTERVAL); + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!seismicAttribute || !timeOrInterval || !data) { + return [0, 0, 0]; + } + const seismicInfo = data.filter( + (seismicInfos) => + seismicInfos.seismicAttribute === seismicAttribute && + seismicInfos.isoDateOrInterval === timeOrInterval, + )[0]; + const zMin = seismicInfo.spec.zOrigin; + const zMax = + seismicInfo.spec.zOrigin + + seismicInfo.spec.zInc * seismicInfo.spec.zFlip * (seismicInfo.spec.numLayers - 1); + const zInc = seismicInfo.spec.zInc; + + return [zMin, zMax, zInc]; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts deleted file mode 100644 index 2b96487be..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceLayer.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { getDepthSliceOptions } from "@api"; -import * as bbox from "@lib/utils/boundingBox"; -import { Geometry, ShapeType } from "@lib/utils/geometry"; -import { rotatePoint2Around } from "@lib/utils/vec2"; -import * as vec3 from "@lib/utils/vec3"; -import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; -import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { QueryClient } from "@tanstack/react-query"; - -import { isEqual } from "lodash"; - -import { RealizationSeismicDepthSliceSettingsContext } from "./RealizationSeismicDepthSliceSettingsContext"; -import { RealizationSeismicDepthSliceSettings, RealizationSeismicDepthSliceStoredData } from "./types"; - -import { SeismicSliceData_trans, transformSeismicSlice } from "../../../settings/queries/queryDataTransforms"; - -export class RealizationSeismicDepthSliceLayer - implements - Layer -{ - private _layerDelegate: LayerDelegate< - RealizationSeismicDepthSliceSettings, - SeismicSliceData_trans, - RealizationSeismicDepthSliceStoredData - >; - private _itemDelegate: ItemDelegate; - - constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Seismic depth slice (realization)", layerManager); - this._layerDelegate = new LayerDelegate( - this, - layerManager, - new RealizationSeismicDepthSliceSettingsContext(layerManager), - LayerColoringType.COLORSCALE - ); - } - - getSettingsContext() { - return this._layerDelegate.getSettingsContext(); - } - - getItemDelegate(): ItemDelegate { - return this._itemDelegate; - } - - getLayerDelegate(): LayerDelegate< - RealizationSeismicDepthSliceSettings, - SeismicSliceData_trans, - RealizationSeismicDepthSliceStoredData - > { - return this._layerDelegate; - } - - makeBoundingBox(): bbox.BBox | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - const settings = this.getSettingsContext().getDelegate().getSettings(); - const seismicDepth = settings[SettingType.SEISMIC_DEPTH_SLICE].getDelegate().getValue(); - - if (seismicDepth === null) { - return null; - } - - // The endpoint returns the bounding box as four points - return bbox.create( - vec3.create( - Math.min(...data.bbox_utm.map((point) => point[0])), - Math.min(...data.bbox_utm.map((point) => point[1])), - seismicDepth - ), - vec3.create( - Math.max(...data.bbox_utm.map((point) => point[0])), - Math.max(...data.bbox_utm.map((point) => point[1])), - seismicDepth - ) - ); - } - - predictNextGeometry(): Geometry | null { - const settings = this.getSettingsContext().getDelegate().getSettings(); - const seismicDepthSliceNumber = settings[SettingType.SEISMIC_DEPTH_SLICE].getDelegate().getValue(); - const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - const isoTimeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - const seismicCubeMeta = this._layerDelegate.getSettingsContext().getDelegate().getStoredData("seismicCubeMeta"); - - if (!seismicCubeMeta || seismicDepthSliceNumber === null) { - return null; - } - - const meta = seismicCubeMeta.find( - (m) => m.seismicAttribute === seismicAttribute && m.isoDateOrInterval === isoTimeOrInterval - ); - - if (!meta) { - return null; - } - - const xmin = meta.spec.xOrigin; - const xmax = meta.spec.xOrigin + meta.spec.xInc * (meta.spec.numCols - 1); - - const ymin = meta.spec.yOrigin; - const ymax = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * (meta.spec.numRows - 1); - - const zmin = seismicDepthSliceNumber; - const zmax = zmin; - - const maxXY = { x: xmax, y: ymax }; - const maxX = { x: xmax, y: ymin }; - const maxY = { x: xmin, y: ymax }; - const origin = { x: meta.spec.xOrigin, y: meta.spec.yOrigin }; - - const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); - const rotatedMaxX = rotatePoint2Around(maxX, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); - const rotatedMaxY = rotatePoint2Around(maxY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); - - const boundingBox = bbox.create( - vec3.create(Math.min(origin.x, rotatedMaxXY.x), Math.min(origin.y, rotatedMaxXY.y), zmin), - vec3.create(Math.max(origin.x, rotatedMaxXY.x), Math.max(origin.y, rotatedMaxXY.y), zmax) - ); - - const geometry: Geometry = { - shapes: [ - { - type: ShapeType.RECTANGLE, - centerPoint: vec3.create((origin.x + rotatedMaxXY.x) / 2, (origin.y + rotatedMaxXY.y) / 2, zmin), - dimensions: { - width: Math.abs(rotatedMaxX.x - origin.x), - height: Math.abs(rotatedMaxY.y - origin.y), - }, - normalizedEdgeVectors: { - u: vec3.normalize(vec3.create(rotatedMaxX.x - origin.x, rotatedMaxX.y - origin.y, 0)), - v: vec3.normalize(vec3.create(rotatedMaxY.x - origin.x, rotatedMaxY.y - origin.y, 0)), - }, - }, - ], - boundingBox, - }; - - return geometry; - } - - doSettingsChangesRequireDataRefetch( - prevSettings: RealizationSeismicDepthSliceSettings, - newSettings: RealizationSeismicDepthSliceSettings - ): boolean { - return !isEqual(prevSettings, newSettings); - } - - makeValueRange(): [number, number] | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return [data.value_min, data.value_max]; - } - - fetchData(queryClient: QueryClient): Promise { - const settings = this.getSettingsContext().getDelegate().getSettings(); - const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); - const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); - const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - - const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - const seismicDepth = settings[SettingType.SEISMIC_DEPTH_SLICE].getDelegate().getValue(); - - const queryKey = [ - "RealizationSeismicDepthSliceSlice", - ensembleIdent, - seismicAttribute, - timeOrInterval, - realizationNum, - seismicDepth, - ]; - this._layerDelegate.registerQueryKey(queryKey); - - const seismicSlicePromise = queryClient - .fetchQuery({ - ...getDepthSliceOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - realization_num: realizationNum ?? 0, - seismic_attribute: seismicAttribute ?? "", - time_or_interval_str: timeOrInterval ?? "", - observed: false, - depth_slice_no: seismicDepth ?? 0, - }, - }), - }) - .then((data) => transformSeismicSlice(data)); - - return seismicSlicePromise; - } - - serializeState(): SerializedLayer { - return this._layerDelegate.serializeState(); - } - - deserializeState(serializedState: SerializedLayer): void { - this._layerDelegate.deserializeState(serializedState); - } -} - -LayerRegistry.registerLayer(RealizationSeismicDepthSliceLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts deleted file mode 100644 index ecdfc311d..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/RealizationSeismicDepthSliceSettingsContext.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { getSeismicCubeMetaListOptions } from "@api"; -import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; -import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; -import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SeismicDepthSliceSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicDepthSliceSetting"; -import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -import { RealizationSeismicDepthSliceSettings, RealizationSeismicDepthSliceStoredData } from "./types"; - -export class RealizationSeismicDepthSliceSettingsContext - implements SettingsContext -{ - private _contextDelegate: SettingsContextDelegate< - RealizationSeismicDepthSliceSettings, - RealizationSeismicDepthSliceStoredData - >; - - constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - RealizationSeismicDepthSliceSettings, - RealizationSeismicDepthSliceStoredData - >(this, layerManager, { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.ATTRIBUTE]: new AttributeSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - [SettingType.SEISMIC_DEPTH_SLICE]: new SeismicDepthSliceSetting(), - }); - } - - areCurrentSettingsValid(settings: RealizationSeismicDepthSliceSettings): boolean { - return ( - settings[SettingType.ENSEMBLE] !== null && - settings[SettingType.REALIZATION] !== null && - settings[SettingType.ATTRIBUTE] !== null && - settings[SettingType.TIME_OR_INTERVAL] !== null - ); - } - - getDelegate(): SettingsContextDelegate< - RealizationSeismicDepthSliceSettings, - RealizationSeismicDepthSliceStoredData - > { - return this._contextDelegate; - } - - getSettings() { - return this._contextDelegate.getSettings(); - } - - defineDependencies({ - helperDependency, - availableSettingsUpdater, - storedDataUpdater, - queryClient, - }: DefineDependenciesArgs) { - availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { - const fieldIdentifier = getGlobalSetting("fieldId"); - const ensembles = getGlobalSetting("ensembles"); - - const ensembleIdents = ensembles - .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) - .map((ensemble) => ensemble.getIdent()); - - return ensembleIdents; - }); - - availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); - - if (!ensembleIdent) { - return []; - } - - const realizations = realizationFilterFunc(ensembleIdent); - - return [...realizations]; - }); - - const realizationSeismicDepthSliceDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realization = getLocalSetting(SettingType.REALIZATION); - - if (!ensembleIdent || realization === null) { - return null; - } - - return await queryClient.fetchQuery({ - ...getSeismicCubeMetaListOptions({ - query: { - case_uuid: ensembleIdent.getCaseUuid(), - ensemble_name: ensembleIdent.getEnsembleName(), - }, - signal: abortSignal, - }), - }); - }); - - storedDataUpdater("seismicCubeMeta", ({ getHelperDependency }) => { - const data = getHelperDependency(realizationSeismicDepthSliceDataDep); - - if (!data) { - return null; - } - - return data; - }); - - availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { - const data = getHelperDependency(realizationSeismicDepthSliceDataDep); - - if (!data) { - return []; - } - - const availableSeismicAttributes = [ - ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismicAttribute))), - ]; - - return availableSeismicAttributes; - }); - - availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); - - const data = getHelperDependency(realizationSeismicDepthSliceDataDep); - - if (!seismicAttribute || !data) { - return []; - } - - const availableTimeOrIntervals = [ - ...Array.from( - new Set( - data - .filter((surface) => surface.seismicAttribute === seismicAttribute) - .map((el) => el.isoDateOrInterval) - ) - ), - ]; - - return availableTimeOrIntervals; - }); - - availableSettingsUpdater(SettingType.SEISMIC_DEPTH_SLICE, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); - const timeOrInterval = getLocalSetting(SettingType.TIME_OR_INTERVAL); - const data = getHelperDependency(realizationSeismicDepthSliceDataDep); - - if (!seismicAttribute || !timeOrInterval || !data) { - return []; - } - const seismicInfo = data.filter( - (seismicInfos) => - seismicInfos.seismicAttribute === seismicAttribute && - seismicInfos.isoDateOrInterval === timeOrInterval - )[0]; - const zMin = seismicInfo.spec.zOrigin; - const zMax = - seismicInfo.spec.zOrigin + - seismicInfo.spec.zInc * seismicInfo.spec.zFlip * (seismicInfo.spec.numLayers - 1); - const zInc = seismicInfo.spec.zInc; - - return [zMin, zMax, zInc]; - }); - } -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/index.ts deleted file mode 100644 index abd4a3635..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { RealizationSeismicDepthSliceLayer } from "./RealizationSeismicDepthSliceLayer"; -export { RealizationSeismicDepthSliceSettingsContext } from "./RealizationSeismicDepthSliceSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts deleted file mode 100644 index 63cd2b539..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { SeismicCubeMeta_api } from "@api"; -import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -export type RealizationSeismicDepthSliceSettings = { - [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; - [SettingType.REALIZATION]: number | null; - [SettingType.ATTRIBUTE]: string | null; - [SettingType.TIME_OR_INTERVAL]: string | null; - [SettingType.SEISMIC_DEPTH_SLICE]: number | null; -}; - -export type RealizationSeismicDepthSliceStoredData = { - seismicCubeMeta: SeismicCubeMeta_api[]; -}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer.ts new file mode 100644 index 000000000..ae1d8f693 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer.ts @@ -0,0 +1,214 @@ +import { type SeismicCubeMeta_api, getInlineSliceOptions, getSeismicCubeMetaListOptions } from "@api"; +import { + type SeismicSliceData_trans, + transformSeismicSlice, +} from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; +import type { + CustomDataLayerImplementation, + DataLayerInformationAccessors, + FetchDataParams, +} from "@modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation"; +import type { DefineDependenciesArgs } from "@modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler"; +import { type MakeSettingTypesMap, Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; + +import { isEqual } from "lodash"; + +const realizationSeismicInlineSettings = [ + Setting.ENSEMBLE, + Setting.REALIZATION, + Setting.ATTRIBUTE, + Setting.TIME_OR_INTERVAL, + Setting.SEISMIC_INLINE, + Setting.COLOR_SCALE, +] as const; +export type RealizationSeismicInlineSettings = typeof realizationSeismicInlineSettings; +type SettingsWithTypes = MakeSettingTypesMap; + +export type RealizationSeismicInlineData = SeismicSliceData_trans; + +export type RealizationSeismicInlineStoredData = { + seismicCubeMeta: SeismicCubeMeta_api[]; +}; + +export class RealizationSeismicInlineLayer + implements + CustomDataLayerImplementation< + RealizationSeismicInlineSettings, + RealizationSeismicInlineData, + RealizationSeismicInlineStoredData + > +{ + settings = realizationSeismicInlineSettings; + + getDefaultName(): string { + return "Seismic Inline (realization)"; + } + + doSettingsChangesRequireDataRefetch(prevSettings: SettingsWithTypes, newSettings: SettingsWithTypes): boolean { + return !isEqual(prevSettings, newSettings); + } + + makeValueRange( + accessors: DataLayerInformationAccessors< + RealizationSeismicInlineSettings, + RealizationSeismicInlineData, + RealizationSeismicInlineStoredData + >, + ): [number, number] | null { + const data = accessors.getData(); + if (!data) { + return null; + } + return [data.value_min, data.value_max]; + } + + fetchData({ + getSetting, + registerQueryKey, + queryClient, + }: FetchDataParams< + RealizationSeismicInlineSettings, + RealizationSeismicInlineData + >): Promise { + const ensembleIdent = getSetting(Setting.ENSEMBLE); + const realizationNum = getSetting(Setting.REALIZATION); + const attribute = getSetting(Setting.ATTRIBUTE); + const timeOrInterval = getSetting(Setting.TIME_OR_INTERVAL); + const inlineNumber = getSetting(Setting.SEISMIC_INLINE); + + const queryOptions = getInlineSliceOptions({ + query: { + case_uuid: ensembleIdent?.getCaseUuid() ?? "", + ensemble_name: ensembleIdent?.getEnsembleName() ?? "", + realization_num: realizationNum ?? 0, + seismic_attribute: attribute ?? "", + time_or_interval_str: timeOrInterval ?? "", + observed: false, + inline_no: inlineNumber ?? 0, + }, + }); + + registerQueryKey(queryOptions.queryKey); + + return queryClient + .fetchQuery({ + ...queryOptions, + }) + .then((data) => transformSeismicSlice(data)); + } + + defineDependencies({ + helperDependency, + availableSettingsUpdater, + storedDataUpdater, + queryClient, + }: DefineDependenciesArgs): void { + availableSettingsUpdater(Setting.ENSEMBLE, ({ getGlobalSetting }) => { + const fieldIdentifier = getGlobalSetting("fieldId"); + const ensembles = getGlobalSetting("ensembles"); + + const ensembleIdents = ensembles + .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) + .map((ensemble) => ensemble.getIdent()); + + return ensembleIdents; + }); + + availableSettingsUpdater(Setting.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { + const ensembleIdent = getLocalSetting(Setting.ENSEMBLE); + const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); + + if (!ensembleIdent) { + return []; + } + + const realizations = realizationFilterFunc(ensembleIdent); + + return [...realizations]; + }); + + const realizationSeismicCrosslineDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { + const ensembleIdent = getLocalSetting(Setting.ENSEMBLE); + const realization = getLocalSetting(Setting.REALIZATION); + + if (!ensembleIdent || realization === null) { + return null; + } + + return await queryClient.fetchQuery({ + ...getSeismicCubeMetaListOptions({ + query: { + case_uuid: ensembleIdent.getCaseUuid(), + ensemble_name: ensembleIdent.getEnsembleName(), + }, + signal: abortSignal, + }), + }); + }); + + storedDataUpdater("seismicCubeMeta", ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!data) { + return null; + } + + return data; + }); + + availableSettingsUpdater(Setting.ATTRIBUTE, ({ getHelperDependency }) => { + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!data) { + return []; + } + + const availableSeismicAttributes = [ + ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismicAttribute))), + ]; + + return availableSeismicAttributes; + }); + + availableSettingsUpdater(Setting.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(Setting.ATTRIBUTE); + + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!seismicAttribute || !data) { + return []; + } + + const availableTimeOrIntervals = [ + ...Array.from( + new Set( + data + .filter((surface) => surface.seismicAttribute === seismicAttribute) + .map((el) => el.isoDateOrInterval), + ), + ), + ]; + + return availableTimeOrIntervals; + }); + + availableSettingsUpdater(Setting.SEISMIC_INLINE, ({ getLocalSetting, getHelperDependency }) => { + const seismicAttribute = getLocalSetting(Setting.ATTRIBUTE); + const timeOrInterval = getLocalSetting(Setting.TIME_OR_INTERVAL); + const data = getHelperDependency(realizationSeismicCrosslineDataDep); + + if (!seismicAttribute || !timeOrInterval || !data) { + return [0, 0, 0]; + } + const seismicInfo = data.filter( + (seismicInfos) => + seismicInfos.seismicAttribute === seismicAttribute && + seismicInfos.isoDateOrInterval === timeOrInterval, + )[0]; + const iMin = 0; + const iMax = seismicInfo.spec.numCols - 1; + + return [iMin, iMax, 1]; + }); + } +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts deleted file mode 100644 index 67aa26f9e..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { getInlineSliceOptions } from "@api"; -import * as bbox from "@lib/utils/boundingBox"; -import { Geometry, ShapeType } from "@lib/utils/geometry"; -import { rotatePoint2Around } from "@lib/utils/vec2"; -import * as vec3 from "@lib/utils/vec3"; -import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; -import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { QueryClient } from "@tanstack/react-query"; - -import { isEqual } from "lodash"; - -import { RealizationSeismicInlineSettingsContext } from "./RealizationSeismicInlineSettingsContext"; -import { RealizationSeismicInlineSettings, RealizationSeismicInlineSliceStoredData } from "./types"; - -import { SeismicSliceData_trans, transformSeismicSlice } from "../../../settings/queries/queryDataTransforms"; - -export class RealizationSeismicInlineLayer - implements Layer -{ - private _layerDelegate: LayerDelegate< - RealizationSeismicInlineSettings, - SeismicSliceData_trans, - RealizationSeismicInlineSliceStoredData - >; - private _itemDelegate: ItemDelegate; - - constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Seismic Inline (realization)", layerManager); - this._layerDelegate = new LayerDelegate( - this, - layerManager, - new RealizationSeismicInlineSettingsContext(layerManager), - LayerColoringType.COLORSCALE - ); - } - - getSettingsContext() { - return this._layerDelegate.getSettingsContext(); - } - - getItemDelegate(): ItemDelegate { - return this._itemDelegate; - } - - getLayerDelegate(): LayerDelegate< - RealizationSeismicInlineSettings, - SeismicSliceData_trans, - RealizationSeismicInlineSliceStoredData - > { - return this._layerDelegate; - } - - doSettingsChangesRequireDataRefetch( - prevSettings: RealizationSeismicInlineSettings, - newSettings: RealizationSeismicInlineSettings - ): boolean { - return !isEqual(prevSettings, newSettings); - } - - makeBoundingBox(): bbox.BBox | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return bbox.create( - vec3.create(data.bbox_utm[0][0], data.bbox_utm[0][1], data.u_min), - vec3.create(data.bbox_utm[1][0], data.bbox_utm[1][1], data.u_max) - ); - } - - predictNextGeometry(): Geometry | null { - const settings = this.getSettingsContext().getDelegate().getSettings(); - const seismicInlineNumber = settings[SettingType.SEISMIC_INLINE].getDelegate().getValue(); - const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - const isoTimeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - const seismicCubeMeta = this._layerDelegate.getSettingsContext().getDelegate().getStoredData("seismicCubeMeta"); - - if (!seismicCubeMeta || seismicInlineNumber === null) { - return null; - } - - const meta = seismicCubeMeta.find( - (m) => m.seismicAttribute === seismicAttribute && m.isoDateOrInterval === isoTimeOrInterval - ); - - if (!meta) { - return null; - } - - const xmin = meta.spec.xOrigin + meta.spec.yInc * seismicInlineNumber; - const xmax = xmin; - - const ymin = meta.spec.yOrigin; - const ymax = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * (meta.spec.numRows - 1); - - const zmin = meta.spec.zOrigin; - const zmax = meta.spec.zOrigin + meta.spec.zInc * meta.spec.zFlip * (meta.spec.numLayers - 1); - - const maxXY = { x: xmax, y: ymax }; - const minXY = { x: xmin, y: ymin }; - const origin = { x: meta.spec.xOrigin, y: meta.spec.yOrigin }; - - const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); - const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); - - const geometry: Geometry = { - shapes: [ - { - type: ShapeType.RECTANGLE, - centerPoint: vec3.create( - (rotatedMinXY.x + rotatedMaxXY.x) / 2, - (rotatedMinXY.y + rotatedMaxXY.y) / 2, - (zmin + zmax) / 2 - ), - dimensions: { - width: vec3.length( - vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0) - ), - height: Math.abs(zmax - zmin), - }, - normalizedEdgeVectors: { - u: vec3.normalize( - vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0) - ), - v: vec3.create(0, 0, 1), - }, - }, - ], - boundingBox: bbox.create( - vec3.create(Math.min(rotatedMinXY.x, rotatedMaxXY.x), Math.min(rotatedMinXY.y, rotatedMaxXY.y), zmin), - vec3.create(Math.max(rotatedMinXY.x, rotatedMaxXY.x), Math.max(rotatedMinXY.y, rotatedMaxXY.y), zmax) - ), - }; - - return geometry; - } - - makeValueRange(): [number, number] | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return [data.value_min, data.value_max]; - } - - fetchData(queryClient: QueryClient): Promise { - const settings = this.getSettingsContext().getDelegate().getSettings(); - const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); - const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); - const seismicAttribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - - const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - const seismicInlineNumber = settings[SettingType.SEISMIC_INLINE].getDelegate().getValue(); - - const queryKey = [ - "realizationSeismicInlineSlice", - ensembleIdent, - seismicAttribute, - timeOrInterval, - realizationNum, - seismicInlineNumber, - ]; - this._layerDelegate.registerQueryKey(queryKey); - - const seismicSlicePromise = queryClient - .fetchQuery({ - ...getInlineSliceOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - realization_num: realizationNum ?? 0, - seismic_attribute: seismicAttribute ?? "", - time_or_interval_str: timeOrInterval ?? "", - observed: false, - inline_no: seismicInlineNumber ?? 0, - }, - }), - }) - .then((data) => transformSeismicSlice(data)); - - return seismicSlicePromise; - } - - serializeState(): SerializedLayer { - return this._layerDelegate.serializeState(); - } - - deserializeState(serializedState: SerializedLayer): void { - this._layerDelegate.deserializeState(serializedState); - } -} - -LayerRegistry.registerLayer(RealizationSeismicInlineLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts deleted file mode 100644 index 61b1d2cea..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineSettingsContext.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { getSeismicCubeMetaListOptions } from "@api"; -import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; -import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; -import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SeismicInlineSetting } from "@modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting"; -import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -import { RealizationSeismicInlineSettings, RealizationSeismicInlineSliceStoredData } from "./types"; - -export class RealizationSeismicInlineSettingsContext - implements SettingsContext -{ - private _contextDelegate: SettingsContextDelegate< - RealizationSeismicInlineSettings, - RealizationSeismicInlineSliceStoredData - >; - - constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate< - RealizationSeismicInlineSettings, - RealizationSeismicInlineSliceStoredData - >(this, layerManager, { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.ATTRIBUTE]: new AttributeSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - [SettingType.SEISMIC_INLINE]: new SeismicInlineSetting(), - }); - } - - areCurrentSettingsValid(settings: RealizationSeismicInlineSettings): boolean { - return ( - settings[SettingType.ENSEMBLE] !== null && - settings[SettingType.REALIZATION] !== null && - settings[SettingType.ATTRIBUTE] !== null && - settings[SettingType.TIME_OR_INTERVAL] !== null - ); - } - - getDelegate(): SettingsContextDelegate { - return this._contextDelegate; - } - - getSettings() { - return this._contextDelegate.getSettings(); - } - - defineDependencies({ - helperDependency, - availableSettingsUpdater, - storedDataUpdater, - queryClient, - }: DefineDependenciesArgs) { - availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { - const fieldIdentifier = getGlobalSetting("fieldId"); - const ensembles = getGlobalSetting("ensembles"); - - const ensembleIdents = ensembles - .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) - .map((ensemble) => ensemble.getIdent()); - - return ensembleIdents; - }); - - availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); - - if (!ensembleIdent) { - return []; - } - - const realizations = realizationFilterFunc(ensembleIdent); - - return [...realizations]; - }); - const realizationSeismicInlineDataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realization = getLocalSetting(SettingType.REALIZATION); - - if (!ensembleIdent || realization === null) { - return null; - } - - return await queryClient.fetchQuery({ - ...getSeismicCubeMetaListOptions({ - query: { - case_uuid: ensembleIdent.getCaseUuid(), - ensemble_name: ensembleIdent.getEnsembleName(), - }, - signal: abortSignal, - }), - }); - }); - - storedDataUpdater("seismicCubeMeta", ({ getHelperDependency }) => { - const data = getHelperDependency(realizationSeismicInlineDataDep); - - if (!data) { - return null; - } - - return data; - }); - - availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { - const data = getHelperDependency(realizationSeismicInlineDataDep); - - if (!data) { - return []; - } - - const availableSeismicAttributes = [ - ...Array.from(new Set(data.map((seismicInfos) => seismicInfos.seismicAttribute))), - ]; - - return availableSeismicAttributes; - }); - - availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); - - const data = getHelperDependency(realizationSeismicInlineDataDep); - - if (!seismicAttribute || !data) { - return []; - } - - const availableTimeOrIntervals = [ - ...Array.from( - new Set( - data - .filter((surface) => surface.seismicAttribute === seismicAttribute) - .map((el) => el.isoDateOrInterval) - ) - ), - ]; - - return availableTimeOrIntervals; - }); - availableSettingsUpdater(SettingType.SEISMIC_INLINE, ({ getLocalSetting, getHelperDependency }) => { - const seismicAttribute = getLocalSetting(SettingType.ATTRIBUTE); - const timeOrInterval = getLocalSetting(SettingType.TIME_OR_INTERVAL); - const data = getHelperDependency(realizationSeismicInlineDataDep); - - if (!seismicAttribute || !timeOrInterval || !data) { - return []; - } - const seismicInfo = data.filter( - (seismicInfos) => - seismicInfos.seismicAttribute === seismicAttribute && - seismicInfos.isoDateOrInterval === timeOrInterval - )[0]; - const iMin = 0; - const iMax = seismicInfo.spec.numCols - 1; - - return [iMin, iMax]; - }); - } -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/index.ts deleted file mode 100644 index 8ee59b2a9..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { RealizationSeismicInlineLayer } from "./RealizationSeismicInlineLayer"; -export { RealizationSeismicInlineSettingsContext } from "./RealizationSeismicInlineSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts deleted file mode 100644 index 335e60d80..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { SeismicCubeMeta_api } from "@api"; -import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -export type RealizationSeismicInlineSettings = { - [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; - [SettingType.REALIZATION]: number | null; - [SettingType.ATTRIBUTE]: string | null; - [SettingType.TIME_OR_INTERVAL]: string | null; - [SettingType.SEISMIC_INLINE]: number | null; -}; - -export type RealizationSeismicInlineSliceStoredData = { - seismicCubeMeta: SeismicCubeMeta_api[]; -}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts deleted file mode 100644 index 07752bb32..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceLayer.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { SurfaceDataPng_api, SurfaceTimeType_api } from "@api"; -import { getSurfaceDataOptions } from "@api"; -import * as bbox from "@lib/utils/boundingBox"; -import * as vec3 from "@lib/utils/vec3"; -import { ItemDelegate } from "@modules/_shared/LayerFramework/delegates/ItemDelegate"; -import { LayerColoringType, LayerDelegate } from "@modules/_shared/LayerFramework/delegates/LayerDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { Layer, SerializedLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; -import { FullSurfaceAddress, SurfaceAddressBuilder } from "@modules/_shared/Surface"; -import { SurfaceDataFloat_trans, transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; -import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; -import { QueryClient } from "@tanstack/react-query"; - -import { isEqual } from "lodash"; - -import { RealizationSurfaceSettingsContext } from "./RealizationSurfaceSettingsContext"; -import { RealizationSurfaceSettings } from "./types"; - -export class RealizationSurfaceLayer - implements Layer -{ - private _layerDelegate: LayerDelegate; - private _itemDelegate: ItemDelegate; - - constructor(layerManager: LayerManager) { - this._itemDelegate = new ItemDelegate("Realization Surface", layerManager); - this._layerDelegate = new LayerDelegate( - this, - layerManager, - new RealizationSurfaceSettingsContext(layerManager), - LayerColoringType.COLORSCALE - ); - } - - getSettingsContext() { - return this._layerDelegate.getSettingsContext(); - } - - getItemDelegate(): ItemDelegate { - return this._itemDelegate; - } - - getLayerDelegate(): LayerDelegate { - return this._layerDelegate; - } - - doSettingsChangesRequireDataRefetch( - prevSettings: RealizationSurfaceSettings, - newSettings: RealizationSurfaceSettings - ): boolean { - return !isEqual(prevSettings, newSettings); - } - - makeBoundingBox(): bbox.BBox | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return { - min: vec3.create(data.transformed_bbox_utm.min_x, data.transformed_bbox_utm.min_y, 0), - max: vec3.create(data.transformed_bbox_utm.max_x, data.transformed_bbox_utm.max_y, 0), - }; - } - - makeValueRange(): [number, number] | null { - const data = this._layerDelegate.getData(); - if (!data) { - return null; - } - - return [data.value_min, data.value_max]; - } - - fetchData(queryClient: QueryClient): Promise { - let surfaceAddress: FullSurfaceAddress | null = null; - const addrBuilder = new SurfaceAddressBuilder(); - - const settings = this.getSettingsContext().getDelegate().getSettings(); - const ensembleIdent = settings[SettingType.ENSEMBLE].getDelegate().getValue(); - const realizationNum = settings[SettingType.REALIZATION].getDelegate().getValue(); - const surfaceName = settings[SettingType.SURFACE_NAME].getDelegate().getValue(); - const attribute = settings[SettingType.ATTRIBUTE].getDelegate().getValue(); - const timeOrInterval = settings[SettingType.TIME_OR_INTERVAL].getDelegate().getValue(); - - if (ensembleIdent && surfaceName && attribute && realizationNum !== null) { - addrBuilder.withEnsembleIdent(ensembleIdent); - addrBuilder.withName(surfaceName); - addrBuilder.withAttribute(attribute); - addrBuilder.withRealization(realizationNum); - - if (timeOrInterval !== SurfaceTimeType_api.NO_TIME) { - addrBuilder.withTimeOrInterval(timeOrInterval); - } - - surfaceAddress = addrBuilder.buildRealizationAddress(); - } - - const surfAddrStr = surfaceAddress ? encodeSurfAddrStr(surfaceAddress) : null; - - const queryKey = ["getSurfaceData", surfAddrStr, null, "float"]; - - this._layerDelegate.registerQueryKey(queryKey); - - const promise = queryClient - .fetchQuery({ - ...getSurfaceDataOptions({ - query: { - surf_addr_str: surfAddrStr ?? "", - data_format: "float", - resample_to_def_str: null, - }, - }), - }) - .then((data) => transformSurfaceData(data)); - - return promise; - } - - serializeState(): SerializedLayer { - return this._layerDelegate.serializeState(); - } - - deserializeState(serializedState: SerializedLayer): void { - this._layerDelegate.deserializeState(serializedState); - } -} - -LayerRegistry.registerLayer(RealizationSurfaceLayer); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts deleted file mode 100644 index d8a99a819..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/RealizationSurfaceSettingsContext.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { SurfaceTimeType_api } from "@api"; -import { getRealizationSurfacesMetadataOptions } from "@api"; -import { SettingsContextDelegate } from "@modules/_shared/LayerFramework/delegates/SettingsContextDelegate"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { DefineDependenciesArgs, SettingsContext } from "@modules/_shared/LayerFramework/interfaces"; -import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; -import { EnsembleSetting } from "@modules/_shared/LayerFramework/settings/implementations/EnsembleSetting"; -import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { SurfaceNameSetting } from "@modules/_shared/LayerFramework/settings/implementations/SurfaceNameSetting"; -import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -import { RealizationSurfaceSettings } from "./types"; - -export class RealizationSurfaceSettingsContext implements SettingsContext { - private _contextDelegate: SettingsContextDelegate; - - constructor(layerManager: LayerManager) { - this._contextDelegate = new SettingsContextDelegate(this, layerManager, { - [SettingType.ENSEMBLE]: new EnsembleSetting(), - [SettingType.REALIZATION]: new RealizationSetting(), - [SettingType.ATTRIBUTE]: new AttributeSetting(), - [SettingType.SURFACE_NAME]: new SurfaceNameSetting(), - [SettingType.TIME_OR_INTERVAL]: new TimeOrIntervalSetting(), - }); - } - - getDelegate(): SettingsContextDelegate { - return this._contextDelegate; - } - - getSettings() { - return this._contextDelegate.getSettings(); - } - - defineDependencies({ - helperDependency, - availableSettingsUpdater, - queryClient, - }: DefineDependenciesArgs) { - availableSettingsUpdater(SettingType.ENSEMBLE, ({ getGlobalSetting }) => { - const fieldIdentifier = getGlobalSetting("fieldId"); - const ensembles = getGlobalSetting("ensembles"); - - const ensembleIdents = ensembles - .filter((ensemble) => ensemble.getFieldIdentifier() === fieldIdentifier) - .map((ensemble) => ensemble.getIdent()); - - return ensembleIdents; - }); - - availableSettingsUpdater(SettingType.REALIZATION, ({ getLocalSetting, getGlobalSetting }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - const realizationFilterFunc = getGlobalSetting("realizationFilterFunction"); - - if (!ensembleIdent) { - return []; - } - - const realizations = realizationFilterFunc(ensembleIdent); - - return [...realizations]; - }); - - const realizationSurfaceMetadataDep = helperDependency(async ({ getLocalSetting, abortSignal }) => { - const ensembleIdent = getLocalSetting(SettingType.ENSEMBLE); - - if (!ensembleIdent) { - return null; - } - - return await queryClient.fetchQuery({ - ...getRealizationSurfacesMetadataOptions({ - query: { - case_uuid: ensembleIdent.getCaseUuid(), - ensemble_name: ensembleIdent.getEnsembleName(), - }, - signal: abortSignal, - }), - }); - }); - - availableSettingsUpdater(SettingType.ATTRIBUTE, ({ getHelperDependency }) => { - const data = getHelperDependency(realizationSurfaceMetadataDep); - - if (!data) { - return []; - } - - const availableAttributes = [ - ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), - ]; - - return availableAttributes; - }); - - availableSettingsUpdater(SettingType.SURFACE_NAME, ({ getHelperDependency, getLocalSetting }) => { - const attribute = getLocalSetting(SettingType.ATTRIBUTE); - const data = getHelperDependency(realizationSurfaceMetadataDep); - - if (!attribute || !data) { - return []; - } - - const availableSurfaceNames = [ - ...Array.from( - new Set( - data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name) - ) - ), - ]; - - return availableSurfaceNames; - }); - - availableSettingsUpdater(SettingType.TIME_OR_INTERVAL, ({ getLocalSetting, getHelperDependency }) => { - const attribute = getLocalSetting(SettingType.ATTRIBUTE); - const surfaceName = getLocalSetting(SettingType.SURFACE_NAME); - const data = getHelperDependency(realizationSurfaceMetadataDep); - - if (!attribute || !surfaceName || !data) { - return []; - } - - const availableTimeOrIntervals: string[] = []; - const availableTimeTypes = [ - ...Array.from( - new Set( - data.surfaces - .filter((surface) => surface.attribute_name === attribute && surface.name === surfaceName) - .map((el) => el.time_type) - ) - ), - ]; - - if (availableTimeTypes.includes(SurfaceTimeType_api.NO_TIME)) { - availableTimeOrIntervals.push(SurfaceTimeType_api.NO_TIME); - } - if (availableTimeTypes.includes(SurfaceTimeType_api.TIME_POINT)) { - availableTimeOrIntervals.push(...data.time_points_iso_str); - } - if (availableTimeTypes.includes(SurfaceTimeType_api.INTERVAL)) { - availableTimeOrIntervals.push(...data.time_intervals_iso_str); - } - - return availableTimeOrIntervals; - }); - } -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts deleted file mode 100644 index ed6642a0d..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { RealizationSurfaceLayer } from "./RealizationSurfaceLayer"; -export { RealizationSurfaceSettingsContext } from "./RealizationSurfaceSettingsContext"; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts deleted file mode 100644 index 48ea63d5b..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSurfaceLayer/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; -import { SettingType } from "@modules/_shared/LayerFramework/settings/settingsTypes"; - -export type RealizationSurfaceSettings = { - [SettingType.ENSEMBLE]: RegularEnsembleIdent | null; - [SettingType.REALIZATION]: number | null; - [SettingType.ATTRIBUTE]: string | null; - [SettingType.SURFACE_NAME]: string | null; - [SettingType.TIME_OR_INTERVAL]: string | null; -}; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer.ts deleted file mode 100644 index a34c34645..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; -import { Grid3DLayer } from "@webviz/subsurface-viewer/dist/layers"; - -import { RealizationGridLayerData } from "../customLayerImplementations/RealizationGridLayer/RealizationGridLayer"; -import { RealizationGridSettings } from "../customLayerImplementations/RealizationGridLayer/types"; - -export function makeGrid3DLayer({ - id, - data, - settings, - colorScale, -}: VisualizationFunctionArgs): Grid3DLayer { - const { gridSurfaceData, gridParameterData } = data; - const offsetXyz = [gridSurfaceData.origin_utm_x, gridSurfaceData.origin_utm_y, 0]; - const pointsData = gridSurfaceData.pointsFloat32Arr.map((val, i) => val + offsetXyz[i % 3]); - const polysData = gridSurfaceData.polysUint32Arr; - - return new Grid3DLayer({ - id, - pointsData, - polysData, - propertiesData: gridParameterData.polyPropsFloat32Arr, - ZIncreasingDownwards: false, - gridLines: settings.showGridLines, - material: { ambient: 0.4, diffuse: 0.7, shininess: 8, specularColor: [25, 25, 25] }, - pickable: true, - colorMapName: "Physics", - colorMapClampColor: true, - colorMapRange: [gridParameterData.min_grid_prop_value, gridParameterData.max_grid_prop_value], - colorMapFunction: makeColorMapFunctionFromColorScale( - colorScale, - gridParameterData.min_grid_prop_value, - gridParameterData.max_grid_prop_value - ), - }); -} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts index 6c30252fc..365776fa3 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts @@ -1,11 +1,11 @@ -import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import type { IntersectionRealizationGridSettings } from "@modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer"; +import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; +import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; import { FenceMeshSection_trans, PolylineIntersection_trans } from "@modules/_shared/utils/wellbore"; import { TGrid3DColoringMode } from "@webviz/subsurface-viewer"; import { Grid3DLayer } from "@webviz/subsurface-viewer/dist/layers"; -import { IntersectionRealizationGridSettings } from "../customLayerImplementations/IntersectionRealizationGridLayer/types"; - interface PolyDataVtk { points: Float32Array; polys: Uint32Array; @@ -77,10 +77,16 @@ function buildVtkStylePolyDataFromFenceSections(fenceSections: FenceMeshSection_ export function makeIntersectionLayer({ id, name, - data, - colorScale, - settings, -}: VisualizationFunctionArgs): Grid3DLayer { + getData, + getSetting, +}: FactoryFunctionArgs): Grid3DLayer | null { + const data = getData(); + const colorScale = getSetting(Setting.COLOR_SCALE)?.colorScale; + const showGridLines = getSetting(Setting.SHOW_GRID_LINES); + + if (!data) { + return null; + } const polyData = buildVtkStylePolyDataFromFenceSections(data.fenceMeshSections); const grid3dIntersectionLayer = new Grid3DLayer({ @@ -96,10 +102,10 @@ export function makeIntersectionLayer({ colorMapFunction: makeColorMapFunctionFromColorScale( colorScale, data.min_grid_prop_value, - data.max_grid_prop_value + data.max_grid_prop_value, ), ZIncreasingDownwards: false, - gridLines: settings.showGridLines, + gridLines: showGridLines, material: { ambient: 0.4, diffuse: 0.7, shininess: 8, specularColor: [25, 25, 25] }, pickable: true, }); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts index c1f0fc07c..63dc054a2 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts @@ -1,35 +1,44 @@ -import { SurfaceDataPng_api } from "@api"; -import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import { + type RealizationSurfaceData, + type RealizationSurfaceSettings, + SurfaceDataFormat, +} from "@modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer"; +import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; +import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; -import { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataTransforms"; import { MapLayer } from "@webviz/subsurface-viewer/dist/layers"; -import { RealizationSurfaceSettings } from "../customLayerImplementations/RealizationSurfaceLayer/types"; - -function isSurfaceDataFloat(apiData: SurfaceDataFloat_trans | SurfaceDataPng_api): apiData is SurfaceDataFloat_trans { - return Object.hasOwn(apiData, "valuesFloat32Arr"); -} - export function makeRealizationSurfaceLayer({ id, name, - data, - colorScale, -}: VisualizationFunctionArgs): MapLayer { - if (isSurfaceDataFloat(data)) { + getData, + getSetting, +}: FactoryFunctionArgs): MapLayer | null { + const data = getData(); + const colorScale = getSetting(Setting.COLOR_SCALE)?.colorScale; + + if (!data) { + return null; + } + + if (data.surfaceData.format === SurfaceDataFormat.FLOAT) { return new MapLayer({ id, name, - meshData: data.valuesFloat32Arr, + meshData: data.surfaceData.valuesFloat32Arr, frame: { - origin: [data.surface_def.origin_utm_x, data.surface_def.origin_utm_y], - count: [data.surface_def.npoints_x, data.surface_def.npoints_y], - increment: [data.surface_def.inc_x, data.surface_def.inc_y], - rotDeg: data.surface_def.rot_deg, + origin: [data.surfaceData.surface_def.origin_utm_x, data.surfaceData.surface_def.origin_utm_y], + count: [data.surfaceData.surface_def.npoints_x, data.surfaceData.surface_def.npoints_y], + increment: [data.surfaceData.surface_def.inc_x, data.surfaceData.surface_def.inc_y], + rotDeg: data.surfaceData.surface_def.rot_deg, }, - valueRange: [data.value_min, data.value_max], - colorMapRange: [data.value_min, data.value_max], - colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max), + valueRange: [data.surfaceData.value_min, data.surfaceData.value_max], + colorMapRange: [data.surfaceData.value_min, data.surfaceData.value_max], + colorMapFunction: makeColorMapFunctionFromColorScale( + colorScale, + data.surfaceData.value_min, + data.surfaceData.value_max, + ), gridLines: false, }); } @@ -37,16 +46,20 @@ export function makeRealizationSurfaceLayer({ return new MapLayer({ id, name, - meshData: data.png_image_base64, + meshData: data.surfaceData.png_image_base64, frame: { - origin: [data.surface_def.origin_utm_x, data.surface_def.origin_utm_y], - count: [data.surface_def.npoints_x, data.surface_def.npoints_y], - increment: [data.surface_def.inc_x, data.surface_def.inc_y], - rotDeg: data.surface_def.rot_deg, + origin: [data.surfaceData.surface_def.origin_utm_x, data.surfaceData.surface_def.origin_utm_y], + count: [data.surfaceData.surface_def.npoints_x, data.surfaceData.surface_def.npoints_y], + increment: [data.surfaceData.surface_def.inc_x, data.surfaceData.surface_def.inc_y], + rotDeg: data.surfaceData.surface_def.rot_deg, }, - valueRange: [data.value_min, data.value_max], - colorMapRange: [data.value_min, data.value_max], - colorMapFunction: makeColorMapFunctionFromColorScale(colorScale, data.value_min, data.value_max), + valueRange: [data.surfaceData.value_min, data.surfaceData.value_max], + colorMapRange: [data.surfaceData.value_min, data.surfaceData.value_max], + colorMapFunction: makeColorMapFunctionFromColorScale( + colorScale, + data.surfaceData.value_min, + data.surfaceData.value_max, + ), gridLines: false, }); } diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index eab9c49cc..f6bb4901d 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -1,25 +1,233 @@ import { Layer } from "@deck.gl/core"; +import * as bbox from "@lib/utils/bbox"; +import { Geometry, ShapeType, degreesToRadians } from "@lib/utils/geometry"; +import { rotatePoint2Around } from "@lib/utils/vec2"; +import * as vec3 from "@lib/utils/vec3"; import { SeismicSliceData_trans } from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; -import { VisualizationFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; +import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; import { SeismicFenceMeshLayer } from "@modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer"; +import type { RealizationSeismicDepthSliceStoredData } from "../customLayerImplementations/RealizationSeismicDepthLayer"; + export enum Plane { CROSSLINE = "CROSSLINE", INLINE = "INLINE", DEPTH = "DEPTH", } +function predictDepthSliceGeometry({ + getSetting, + getStoredData, +}: FactoryFunctionArgs): Geometry | null { + const attribute = getSetting(Setting.ATTRIBUTE); + const timeOrInterval = getSetting(Setting.TIME_OR_INTERVAL); + const seismicDepthSliceNumber = getSetting(Setting.SEISMIC_DEPTH_SLICE); + const seismicCubeMeta = getStoredData("seismicCubeMeta"); + + if (!seismicCubeMeta || seismicDepthSliceNumber === null) { + return null; + } + + const meta = seismicCubeMeta.find( + (m) => m.seismicAttribute === attribute && m.isoDateOrInterval === timeOrInterval, + ); + + if (!meta) { + return null; + } + + const xmin = meta.spec.xOrigin; + const xmax = meta.spec.xOrigin + meta.spec.xInc * (meta.spec.numCols - 1); + + const ymin = meta.spec.yOrigin; + const ymax = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * (meta.spec.numRows - 1); + + const zmin = seismicDepthSliceNumber; + const zmax = zmin; + + const maxXY = { x: xmax, y: ymax }; + const maxX = { x: xmax, y: ymin }; + const maxY = { x: xmin, y: ymax }; + const origin = { x: meta.spec.xOrigin, y: meta.spec.yOrigin }; + + const rotatedMaxXY = rotatePoint2Around(maxXY, origin, degreesToRadians(meta.spec.rotationDeg)); + const rotatedMaxX = rotatePoint2Around(maxX, origin, degreesToRadians(meta.spec.rotationDeg)); + const rotatedMaxY = rotatePoint2Around(maxY, origin, degreesToRadians(meta.spec.rotationDeg)); + + const boundingBox = bbox.create( + vec3.create(Math.min(origin.x, rotatedMaxXY.x), Math.min(origin.y, rotatedMaxXY.y), zmin), + vec3.create(Math.max(origin.x, rotatedMaxXY.x), Math.max(origin.y, rotatedMaxXY.y), zmax), + ); + + const geometry: Geometry = { + shapes: [ + { + type: ShapeType.RECTANGLE, + centerPoint: vec3.create((origin.x + rotatedMaxXY.x) / 2, (origin.y + rotatedMaxXY.y) / 2, zmin), + dimensions: { + width: Math.abs(rotatedMaxX.x - origin.x), + height: Math.abs(rotatedMaxY.y - origin.y), + }, + normalizedEdgeVectors: { + u: vec3.normalize(vec3.create(rotatedMaxX.x - origin.x, rotatedMaxX.y - origin.y, 0)), + v: vec3.normalize(vec3.create(rotatedMaxY.x - origin.x, rotatedMaxY.y - origin.y, 0)), + }, + }, + ], + boundingBox, + }; + + return geometry; +} + +function predictCrosslineGeometry({ + getSetting, + getStoredData, +}: FactoryFunctionArgs): Geometry | null { + const attribute = getSetting(Setting.ATTRIBUTE); + const timeOrInterval = getSetting(Setting.TIME_OR_INTERVAL); + const seismicCrosslineNumber = getSetting(Setting.SEISMIC_CROSSLINE); + const seismicCubeMeta = getStoredData("seismicCubeMeta"); + + if (!seismicCubeMeta || seismicCrosslineNumber === null) { + return null; + } + + const meta = seismicCubeMeta.find( + (m) => m.seismicAttribute === attribute && m.isoDateOrInterval === timeOrInterval, + ); + + if (!meta) { + return null; + } + + const xmin = meta.spec.xOrigin; + const xmax = meta.spec.xOrigin + meta.spec.xInc * (meta.spec.numCols - 1); + + const ymin = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * seismicCrosslineNumber; + const ymax = ymin; + + const zmin = meta.spec.zOrigin; + const zmax = meta.spec.zOrigin + meta.spec.zInc * meta.spec.zFlip * (meta.spec.numLayers - 1); + + const maxXY = { x: xmax, y: ymax }; + const minXY = { x: xmin, y: ymin }; + const origin = { x: meta.spec.xOrigin, y: meta.spec.yOrigin }; + + const rotatedMinXY = rotatePoint2Around(minXY, origin, degreesToRadians(meta.spec.rotationDeg)); + const rotatedMaxXY = rotatePoint2Around(maxXY, origin, degreesToRadians(meta.spec.rotationDeg)); + + const geometry: Geometry = { + shapes: [ + { + type: ShapeType.RECTANGLE, + centerPoint: vec3.create( + (rotatedMinXY.x + rotatedMaxXY.x) / 2, + (rotatedMinXY.y + rotatedMaxXY.y) / 2, + (zmin + zmax) / 2, + ), + dimensions: { + width: vec3.length( + vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0), + ), + height: Math.abs(zmax - zmin), + }, + normalizedEdgeVectors: { + u: vec3.normalize(vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0)), + v: vec3.create(0, 0, 1), + }, + }, + ], + boundingBox: bbox.create( + vec3.create(Math.min(rotatedMinXY.x, rotatedMaxXY.x), Math.min(rotatedMinXY.y, rotatedMaxXY.y), zmin), + vec3.create(Math.max(rotatedMinXY.x, rotatedMaxXY.x), Math.max(rotatedMinXY.y, rotatedMaxXY.y), zmax), + ), + }; + + return geometry; +} + +function predictInlineGeometry({ + getSetting, + getStoredData, +}: FactoryFunctionArgs): Geometry | null { + const attribute = getSetting(Setting.ATTRIBUTE); + const timeOrInterval = getSetting(Setting.TIME_OR_INTERVAL); + const seismicInlineNumber = getSetting(Setting.SEISMIC_INLINE); + const seismicCubeMeta = getStoredData("seismicCubeMeta"); + + if (!seismicCubeMeta || seismicInlineNumber === null) { + return null; + } + + const meta = seismicCubeMeta.find( + (m) => m.seismicAttribute === attribute && m.isoDateOrInterval === timeOrInterval, + ); + + if (!meta) { + return null; + } + + const xmin = meta.spec.xOrigin + meta.spec.yInc * seismicInlineNumber; + const xmax = xmin; + + const ymin = meta.spec.yOrigin; + const ymax = meta.spec.yOrigin + meta.spec.yInc * meta.spec.yFlip * (meta.spec.numRows - 1); + + const zmin = meta.spec.zOrigin; + const zmax = meta.spec.zOrigin + meta.spec.zInc * meta.spec.zFlip * (meta.spec.numLayers - 1); + + const maxXY = { x: xmax, y: ymax }; + const minXY = { x: xmin, y: ymin }; + const origin = { x: meta.spec.xOrigin, y: meta.spec.yOrigin }; + + const rotatedMinXY = rotatePoint2Around(minXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); + const rotatedMaxXY = rotatePoint2Around(maxXY, origin, (meta.spec.rotationDeg / 180.0) * Math.PI); + + const geometry: Geometry = { + shapes: [ + { + type: ShapeType.RECTANGLE, + centerPoint: vec3.create( + (rotatedMinXY.x + rotatedMaxXY.x) / 2, + (rotatedMinXY.y + rotatedMaxXY.y) / 2, + (zmin + zmax) / 2, + ), + dimensions: { + width: vec3.length( + vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0), + ), + height: Math.abs(zmax - zmin), + }, + normalizedEdgeVectors: { + u: vec3.normalize(vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0)), + v: vec3.create(0, 0, 1), + }, + }, + ], + boundingBox: bbox.create( + vec3.create(Math.min(rotatedMinXY.x, rotatedMaxXY.x), Math.min(rotatedMinXY.y, rotatedMaxXY.y), zmin), + vec3.create(Math.max(rotatedMinXY.x, rotatedMaxXY.x), Math.max(rotatedMinXY.y, rotatedMaxXY.y), zmax), + ), + }; + + return geometry; +} + export function makeSeismicFenceMeshLayerFunction(plane: Plane) { - return function makeSeismicFenceMeshLayer({ - id, - name, - data, - colorScale, - settings, - isLoading, - predictedGeometry, - }: VisualizationFunctionArgs): Layer { + return function makeSeismicFenceMeshLayer( + args: FactoryFunctionArgs, + ): Layer | null { + const { id, name, getData, getSetting, isLoading } = args; + const data = getData(); + const colorScale = getSetting("colorScale")?.colorScale; + + if (!data) { + return null; + } + let bbox: number[][] = [ [data.bbox_utm[0][0], data.bbox_utm[0][1], data.u_min], [data.bbox_utm[1][0], data.bbox_utm[1][1], data.u_min], @@ -27,13 +235,22 @@ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { [data.bbox_utm[1][0], data.bbox_utm[1][1], data.u_max], ]; + let predictedGeometry: Geometry | null = null; + if (plane === Plane.DEPTH) { + const seismicDepthSlice = getSetting(Setting.SEISMIC_DEPTH_SLICE); bbox = [ - [data.bbox_utm[0][0], data.bbox_utm[0][1], settings.seismicDepthSlice], - [data.bbox_utm[3][0], data.bbox_utm[3][1], settings.seismicDepthSlice], - [data.bbox_utm[1][0], data.bbox_utm[1][1], settings.seismicDepthSlice], - [data.bbox_utm[2][0], data.bbox_utm[2][1], settings.seismicDepthSlice], + [data.bbox_utm[0][0], data.bbox_utm[0][1], seismicDepthSlice], + [data.bbox_utm[3][0], data.bbox_utm[3][1], seismicDepthSlice], + [data.bbox_utm[1][0], data.bbox_utm[1][1], seismicDepthSlice], + [data.bbox_utm[2][0], data.bbox_utm[2][1], seismicDepthSlice], ]; + + predictedGeometry = predictDepthSliceGeometry(args); + } else if (plane === Plane.CROSSLINE) { + predictedGeometry = predictCrosslineGeometry(args); + } else if (plane === Plane.INLINE) { + predictedGeometry = predictInlineGeometry(args); } return new SeismicFenceMeshLayer({ diff --git a/frontend/src/modules/3DViewerNew/interfaces.ts b/frontend/src/modules/3DViewerNew/interfaces.ts index 914b5a703..af3c0ed71 100644 --- a/frontend/src/modules/3DViewerNew/interfaces.ts +++ b/frontend/src/modules/3DViewerNew/interfaces.ts @@ -3,10 +3,10 @@ import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponen import { layerManagerAtom, preferredViewLayoutAtom } from "./settings/atoms/baseAtoms"; import { PreferredViewLayout } from "./types"; -import { LayerManager } from "../_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { DataLayerManager } from "../_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; export type SettingsToViewInterface = { - layerManager: LayerManager | null; + layerManager: DataLayerManager | null; preferredViewLayout: PreferredViewLayout; }; diff --git a/frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts b/frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts index 2ebacfc7e..b50cdb3fe 100644 --- a/frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts +++ b/frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts @@ -1,8 +1,8 @@ import { PreferredViewLayout } from "@modules/2DViewer/types"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import type { DataLayerManager } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; import { atom } from "jotai"; export const userSelectedFieldIdentifierAtom = atom(null); -export const layerManagerAtom = atom(null); +export const layerManagerAtom = atom(null); export const preferredViewLayoutAtom = atom(PreferredViewLayout.VERTICAL); diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index 9de378db8..01d3003a0 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -8,28 +8,28 @@ import { Menu } from "@lib/components/Menu"; import { MenuButton } from "@lib/components/MenuButton"; import { MenuHeading } from "@lib/components/MenuHeading"; import { MenuItem } from "@lib/components/MenuItem"; -import { ObservedSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer"; -import { StatisticalSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer"; import { PreferredViewLayout } from "@modules/2DViewer/types"; -import { IntersectionRealizationGridLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer"; -import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; -import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer"; -import { EnsembleSetting } from "@modules//_shared/LayerFramework/settings/implementations/EnsembleSetting"; import { LayersActionGroup } from "@modules/_shared/LayerFramework/LayersActions"; import { GroupDelegate, GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; -import { ColorScale } from "@modules/_shared/LayerFramework/framework/ColorScale/ColorScale"; +import { DataLayer } from "@modules/_shared/LayerFramework/framework/DataLayer/DataLayer"; +import type { DataLayerManager } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; +import { LayerManagerComponent } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManagerComponent"; import { DeltaSurface } from "@modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurface"; -import { LayerManager } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; -import { LayerManagerComponent } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManagerComponent"; +import { Group } from "@modules/_shared/LayerFramework/framework/Group/Group"; import { SettingsGroup } from "@modules/_shared/LayerFramework/framework/SettingsGroup/SettingsGroup"; import { SharedSetting } from "@modules/_shared/LayerFramework/framework/SharedSetting/SharedSetting"; -import { View } from "@modules/_shared/LayerFramework/framework/View/View"; -import { Group, Item, instanceofGroup, instanceofLayer } from "@modules/_shared/LayerFramework/interfaces"; -import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; -import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; -import { AttributeSetting } from "@modules/_shared/LayerFramework/settings/implementations/AttributeSetting"; -import { RealizationSetting } from "@modules/_shared/LayerFramework/settings/implementations/RealizationSetting"; -import { TimeOrIntervalSetting } from "@modules/_shared/LayerFramework/settings/implementations/TimeOrIntervalSetting"; +import { GroupRegistry } from "@modules/_shared/LayerFramework/groups/GroupRegistry"; +import { + type Item, + type ItemGroup, + instanceofItemGroup, +} from "@modules/_shared/LayerFramework/interfacesAndTypes/entitites"; +import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; +import { ObservedSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/ObservedSurfaceLayer"; +import { RealizationSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer"; +import { StatisticalSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer"; +import { LayerType } from "@modules/_shared/LayerFramework/layers/layerTypes"; +import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { Dropdown } from "@mui/base"; import { @@ -43,13 +43,10 @@ import { import { useAtom } from "jotai"; -import { RealizationGridLayer } from "../../LayerFramework/customLayerImplementations/RealizationGridLayer"; -import { RealizationSeismicInlineLayer } from "../../LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer/RealizationSeismicInlineLayer"; -import { RealizationSurfaceLayer } from "../../LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; import { preferredViewLayoutAtom } from "../atoms/baseAtoms"; export type LayerManagerComponentWrapperProps = { - layerManager: LayerManager; + layerManager: DataLayerManager; workbenchSession: WorkbenchSession; workbenchSettings: WorkbenchSettings; }; @@ -62,71 +59,81 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper usePublishSubscribeTopicValue(groupDelegate, GroupDelegateTopic.CHILDREN); function handleLayerAction(identifier: string, groupDelegate: GroupDelegate) { - const numSharedSettings = groupDelegate.findChildren((item) => { - return item instanceof SharedSetting; - }).length; - - const numViews = groupDelegate.getDescendantItems((item) => item instanceof View).length; - switch (identifier) { case "view": - groupDelegate.appendChild( - new View(numViews > 0 ? `View (${numViews})` : "View", props.layerManager, colorSet.getNextColor()) - ); + groupDelegate.appendChild(GroupRegistry.makeGroup("View", props.layerManager, colorSet.getNextColor())); return; case "delta-surface": - groupDelegate.insertChild(new DeltaSurface("Delta surface", props.layerManager), numSharedSettings); + groupDelegate.appendChild(new DeltaSurface("Delta surface", props.layerManager)); return; case "settings-group": - groupDelegate.insertChild(new SettingsGroup("Settings group", props.layerManager), numSharedSettings); + groupDelegate.appendChild(new SettingsGroup("Settings group", props.layerManager)); return; case "color-scale": - groupDelegate.prependChild(new ColorScale("Color scale", props.layerManager)); + groupDelegate.appendChild(new SharedSetting(Setting.COLOR_SCALE, null, props.layerManager)); return; - case "intersection-realization-grid": - groupDelegate.insertChild(new IntersectionRealizationGridLayer(props.layerManager), numSharedSettings); + case "observed-surface": + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.OBSERVED_SURFACE, props.layerManager)); return; - case "realization-grid": - groupDelegate.insertChild(new RealizationGridLayer(props.layerManager), numSharedSettings); + case "statistical-surface": + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.STATISTICAL_SURFACE, props.layerManager)); return; case "realization-surface": - groupDelegate.insertChild(new RealizationSurfaceLayer(props.layerManager), numSharedSettings); + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_SURFACE, props.layerManager)); + return; + case "realization-polygons": + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_POLYGONS, props.layerManager)); + return; + case "drilled-wellbore-trajectories": + groupDelegate.prependChild( + LayerRegistry.makeLayer(LayerType.DRILLED_WELL_TRAJECTORIES, props.layerManager), + ); + return; + case "drilled-wellbore-picks": + groupDelegate.prependChild( + LayerRegistry.makeLayer(LayerType.DRILLED_WELLBORE_PICKS, props.layerManager), + ); + return; + case "realization-grid": + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_GRID, props.layerManager)); return; case "realization-seismic-inline": - groupDelegate.insertChild(new RealizationSeismicInlineLayer(props.layerManager), numSharedSettings); + groupDelegate.prependChild( + LayerRegistry.makeLayer(LayerType.REALIZATION_SEISMIC_INLINE, props.layerManager), + ); return; case "realization-seismic-crossline": - groupDelegate.insertChild(new RealizationSeismicCrosslineLayer(props.layerManager), numSharedSettings); + groupDelegate.prependChild( + LayerRegistry.makeLayer(LayerType.REALIZATION_SEISMIC_CROSSLINE, props.layerManager), + ); return; case "realization-seismic-depth-slice": - groupDelegate.insertChild(new RealizationSeismicDepthSliceLayer(props.layerManager), numSharedSettings); - return; - case "drilled-wellbore-trajectories": - groupDelegate.insertChild(new DrilledWellTrajectoriesLayer(props.layerManager), numSharedSettings); - return; - - case "drilled-wellbore-picks": - groupDelegate.insertChild(new DrilledWellborePicksLayer(props.layerManager), numSharedSettings); + groupDelegate.prependChild( + LayerRegistry.makeLayer(LayerType.REALIZATION_SEISMIC_DEPTH_SLICE, props.layerManager), + ); return; case "ensemble": - groupDelegate.prependChild(new SharedSetting(new EnsembleSetting(), props.layerManager)); + groupDelegate.appendChild(new SharedSetting(Setting.ENSEMBLE, null, props.layerManager)); return; case "realization": - groupDelegate.prependChild(new SharedSetting(new RealizationSetting(), props.layerManager)); + groupDelegate.appendChild(new SharedSetting(Setting.REALIZATION, null, props.layerManager)); + return; + case "surface-name": + groupDelegate.appendChild(new SharedSetting(Setting.SURFACE_NAME, null, props.layerManager)); return; case "attribute": - groupDelegate.prependChild(new SharedSetting(new AttributeSetting(), props.layerManager)); + groupDelegate.appendChild(new SharedSetting(Setting.ATTRIBUTE, null, props.layerManager)); return; case "Date": - groupDelegate.prependChild(new SharedSetting(new TimeOrIntervalSetting(), props.layerManager)); + groupDelegate.appendChild(new SharedSetting(Setting.TIME_OR_INTERVAL, null, props.layerManager)); return; } } - function checkIfItemMoveAllowed(movedItem: Item, destinationItem: Group): boolean { + function checkIfItemMoveAllowed(movedItem: Item, destinationItem: ItemGroup): boolean { if (destinationItem instanceof DeltaSurface) { if ( - instanceofLayer(movedItem) && + movedItem instanceof DataLayer && !( movedItem instanceof RealizationSurfaceLayer || movedItem instanceof StatisticalSurfaceLayer || @@ -136,11 +143,11 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper return false; } - if (instanceofGroup(movedItem)) { + if (instanceofItemGroup(movedItem)) { return false; } - if (destinationItem.getGroupDelegate().findChildren((item) => instanceofLayer(item)).length >= 2) { + if (destinationItem.getGroupDelegate().findChildren((item) => item instanceof DataLayer).length >= 2) { return false; } } @@ -148,7 +155,8 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper return true; } - const hasView = groupDelegate.getDescendantItems((item) => item instanceof View).length > 0; + const hasView = + groupDelegate.getDescendantItems((item) => item instanceof Group && item.getGroupType() === "View").length > 0; const adjustedLayerActions = hasView ? LAYER_ACTIONS : INITIAL_LAYER_ACTIONS; return ( diff --git a/frontend/src/modules/3DViewerNew/settings/settings.tsx b/frontend/src/modules/3DViewerNew/settings/settings.tsx index aad2cc170..de8845451 100644 --- a/frontend/src/modules/3DViewerNew/settings/settings.tsx +++ b/frontend/src/modules/3DViewerNew/settings/settings.tsx @@ -13,7 +13,10 @@ import { layerManagerAtom, preferredViewLayoutAtom, userSelectedFieldIdentifierA import { selectedFieldIdentifierAtom } from "./atoms/derivedAtoms"; import { LayerManagerComponentWrapper } from "./components/layerManagerComponentWrapper"; -import { LayerManager, LayerManagerTopic } from "../../_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { + DataLayerManager, + LayerManagerTopic, +} from "../../_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; export function Settings(props: ModuleSettingsProps): React.ReactNode { const ensembleSet = useEnsembleSet(props.workbenchSession); @@ -38,16 +41,16 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode { }; window.localStorage.setItem( `${props.settingsContext.getInstanceIdString()}-settings`, - JSON.stringify(serializedState) + JSON.stringify(serializedState), ); }, - [layerManager, fieldIdentifier, preferredViewLayout, props.settingsContext] + [layerManager, fieldIdentifier, preferredViewLayout, props.settingsContext], ); const applyPersistedState = React.useCallback( - function applyPersistedState(layerManager: LayerManager) { + function applyPersistedState(layerManager: DataLayerManager) { const serializedState = window.localStorage.getItem( - `${props.settingsContext.getInstanceIdString()}-settings` + `${props.settingsContext.getInstanceIdString()}-settings`, ); if (!serializedState) { return; @@ -68,12 +71,12 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode { layerManager.deserializeState(parsedState.layerManager); } }, - [setFieldIdentifier, setPreferredViewLayout, props.settingsContext] + [setFieldIdentifier, setPreferredViewLayout, props.settingsContext], ); React.useEffect( function onMountEffect() { - const newLayerManager = new LayerManager(props.workbenchSession, props.workbenchSettings, queryClient); + const newLayerManager = new DataLayerManager(props.workbenchSession, props.workbenchSettings, queryClient); setLayerManager(newLayerManager); applyPersistedState(newLayerManager); @@ -82,7 +85,7 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode { newLayerManager.beforeDestroy(); }; }, - [setLayerManager, props.workbenchSession, props.workbenchSettings, queryClient, applyPersistedState] + [setLayerManager, props.workbenchSession, props.workbenchSettings, queryClient, applyPersistedState], ); React.useEffect( @@ -108,7 +111,7 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode { unsubscribeExpands(); }; }, - [layerManager, props.workbenchSession, props.workbenchSettings, persistState] + [layerManager, props.workbenchSession, props.workbenchSettings, persistState], ); React.useEffect( @@ -118,7 +121,7 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode { } layerManager.updateGlobalSetting("fieldId", fieldIdentifier); }, - [fieldIdentifier, layerManager] + [fieldIdentifier, layerManager], ); function handleFieldChange(fieldId: string | null) { diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 6e4365953..6664e8dd0 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -7,33 +7,47 @@ import { WorkbenchSession } from "@framework/WorkbenchSession"; import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { useElementSize } from "@lib/hooks/useElementSize"; import * as bbox from "@lib/utils/boundingBox"; -import { RealizationSurfaceLayer } from "@modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer"; +import { makeColorScaleAnnotation } from "@modules/2DViewer/LayerFramework/annotations/makeColorScaleAnnotation"; import { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; -import { IntersectionRealizationGridLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/IntersectionRealizationGridLayer"; -import { RealizationGridLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationGridLayer"; import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; -import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthSliceLayer"; +import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer"; import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; -import { makeGrid3DLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeGrid3dLayer"; -import { makeIntersectionLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer"; import { makeRealizationSurfaceLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer"; import { Plane, makeSeismicFenceMeshLayerFunction, } from "@modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer"; -import { LayerManager, LayerManagerTopic } from "@modules/_shared/LayerFramework/framework/LayerManager/LayerManager"; +import { + type DataLayerManager, + LayerManagerTopic, +} from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; +import { ObservedSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/ObservedSurfaceLayer"; +import { RealizationGridLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer"; +import { RealizationPolygonsLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationPolygonsLayer"; +import { RealizationSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer"; +import { StatisticalSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer"; +import { LayerType } from "@modules/_shared/LayerFramework/layers/layerTypes"; import { + type Annotation, LayerWithPosition, VisualizationFactory, VisualizationTarget, } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import { makeWellborePicksLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer"; -import { makeWellsLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer"; +import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellTrajectoriesBoundingBox"; +import { makeDrilledWellborePicksBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellborePicksBoundingBox"; +import { makePolygonDataBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makePolygonDataBoundingBox"; +import { makeRealizationGridBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationGridBoundingBox"; +import { makeSurfaceLayerBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeSurfaceLayerBoundingBox"; +import { makeDrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer"; +import { makeDrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellborePicksLayer"; +import { makeObservedSurfaceLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeObservedSurfaceLayer"; +import { makeRealizationGridLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer"; +import { makeRealizationPolygonsLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationPolygonsLayer"; +import { makeStatisticalSurfaceLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeStatisticalSurfaceLayer"; import { ColorLegendsContainer } from "@modules/_shared/components/ColorLegendsContainer"; -import { ColorScaleWithId } from "@modules/_shared/components/ColorLegendsContainer/colorLegendsContainer"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { BoundingBox3D, ViewportType } from "@webviz/subsurface-viewer"; import { ViewsType } from "@webviz/subsurface-viewer/dist/SubsurfaceViewer"; @@ -44,26 +58,60 @@ import { InteractionWrapper } from "./InteractionWrapper"; import { PlaceholderLayer } from "../../../_shared/customDeckGlLayers/PlaceholderLayer"; const VISUALIZATION_FACTORY = new VisualizationFactory(); -VISUALIZATION_FACTORY.registerVisualizationFunction(DrilledWellborePicksLayer, makeWellborePicksLayer); -VISUALIZATION_FACTORY.registerVisualizationFunction(DrilledWellTrajectoriesLayer, makeWellsLayer); -VISUALIZATION_FACTORY.registerVisualizationFunction(RealizationGridLayer, makeGrid3DLayer); -VISUALIZATION_FACTORY.registerVisualizationFunction(IntersectionRealizationGridLayer, makeIntersectionLayer); -VISUALIZATION_FACTORY.registerVisualizationFunction( - RealizationSeismicCrosslineLayer, - makeSeismicFenceMeshLayerFunction(Plane.CROSSLINE) -); -VISUALIZATION_FACTORY.registerVisualizationFunction( - RealizationSeismicInlineLayer, - makeSeismicFenceMeshLayerFunction(Plane.INLINE) -); -VISUALIZATION_FACTORY.registerVisualizationFunction( + +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.OBSERVED_SURFACE, ObservedSurfaceLayer, { + makeVisualizationFunction: makeObservedSurfaceLayer, + calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, + makeAnnotationsFunction: makeColorScaleAnnotation, +}); +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_SURFACE, RealizationSurfaceLayer, { + makeVisualizationFunction: makeRealizationSurfaceLayer, + calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, + makeAnnotationsFunction: makeColorScaleAnnotation, +}); +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.STATISTICAL_SURFACE, StatisticalSurfaceLayer, { + makeVisualizationFunction: makeStatisticalSurfaceLayer, + calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, + makeAnnotationsFunction: makeColorScaleAnnotation, +}); +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_POLYGONS, RealizationPolygonsLayer, { + makeVisualizationFunction: makeRealizationPolygonsLayer, + calculateBoundingBoxFunction: makePolygonDataBoundingBox, + makeAnnotationsFunction: makeColorScaleAnnotation, +}); +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_GRID, RealizationGridLayer, { + makeVisualizationFunction: makeRealizationGridLayer, + calculateBoundingBoxFunction: makeRealizationGridBoundingBox, + makeAnnotationsFunction: makeColorScaleAnnotation, +}); +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.DRILLED_WELLBORE_PICKS, DrilledWellborePicksLayer, { + makeVisualizationFunction: makeDrilledWellborePicksLayer, + calculateBoundingBoxFunction: makeDrilledWellborePicksBoundingBox, +}); +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.DRILLED_WELL_TRAJECTORIES, DrilledWellTrajectoriesLayer, { + makeVisualizationFunction: makeDrilledWellTrajectoriesLayer, + calculateBoundingBoxFunction: makeDrilledWellTrajectoriesBoundingBox, +}); +VISUALIZATION_FACTORY.registerLayerFunctions( + LayerType.REALIZATION_SEISMIC_DEPTH_SLICE, RealizationSeismicDepthSliceLayer, - makeSeismicFenceMeshLayerFunction(Plane.DEPTH) + { + makeVisualizationFunction: makeSeismicFenceMeshLayerFunction(Plane.DEPTH), + }, +); +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_SEISMIC_INLINE, RealizationSeismicInlineLayer, { + makeVisualizationFunction: makeSeismicFenceMeshLayerFunction(Plane.INLINE), +}); +VISUALIZATION_FACTORY.registerLayerFunctions( + LayerType.REALIZATION_SEISMIC_CROSSLINE, + RealizationSeismicCrosslineLayer, + { + makeVisualizationFunction: makeSeismicFenceMeshLayerFunction(Plane.CROSSLINE), + }, ); -VISUALIZATION_FACTORY.registerVisualizationFunction(RealizationSurfaceLayer, makeRealizationSurfaceLayer); export type LayersWrapperProps = { - layerManager: LayerManager; + layerManager: DataLayerManager; preferredViewLayout: PreferredViewLayout; viewContext: ViewContext; workbenchSession: WorkbenchSession; @@ -82,7 +130,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { const viewports: ViewportType[] = []; const viewerLayers: LayerWithPosition[] = []; const viewportAnnotations: React.ReactNode[] = []; - const globalColorScales: ColorScaleWithId[] = []; + const globalAnnotations: Annotation[] = []; const views: ViewsType = { layout: [1, 1], @@ -93,10 +141,10 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { let numCols = 0; let numRows = 0; - const viewsAndLayers = VISUALIZATION_FACTORY.make(props.layerManager); + const factoryProduct = VISUALIZATION_FACTORY.make(props.layerManager); - numCols = Math.ceil(Math.sqrt(viewsAndLayers.views.length)); - numRows = Math.ceil(viewsAndLayers.views.length / numCols); + numCols = Math.ceil(Math.sqrt(factoryProduct.views.length)); + numRows = Math.ceil(factoryProduct.views.length / numCols); if (props.preferredViewLayout === PreferredViewLayout.HORIZONTAL) { [numCols, numRows] = [numRows, numCols]; @@ -104,11 +152,11 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { views.layout = [numCols, numRows]; - viewerLayers.push(...viewsAndLayers.layers); - globalColorScales.push(...viewsAndLayers.colorScales); - const globalLayerIds = viewsAndLayers.layers.map((layer) => layer.layer.id); + viewerLayers.push(...factoryProduct.layers); + globalAnnotations.push(...factoryProduct.annotations); + const globalLayerIds = factoryProduct.layers.map((layer) => layer.layer.id); - for (const view of viewsAndLayers.views) { + for (const view of factoryProduct.views) { viewports.push({ id: view.id, name: view.name, @@ -131,12 +179,12 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { /* @ts-expect-error */ "colorScale" in el), ...globalAnnotations]} height={((mainDivSize.height / 3) * 2) / numCols - 20} position="left" />
-
+
{view.name}
- + , ); } - if (viewsAndLayers.boundingBox !== null) { + if (factoryProduct.combinedBoundingBox !== null) { if (prevBoundingBox !== null) { - if (!bbox.outerBoxcontainsInnerBox(prevBoundingBox, viewsAndLayers.boundingBox)) { - setPrevBoundingBox(viewsAndLayers.boundingBox); + if (!bbox.outerBoxcontainsInnerBox(prevBoundingBox, factoryProduct.combinedBoundingBox)) { + setPrevBoundingBox(factoryProduct.combinedBoundingBox); } } else { - setPrevBoundingBox(viewsAndLayers.boundingBox); + setPrevBoundingBox(factoryProduct.combinedBoundingBox); } } - statusWriter.setLoading(viewsAndLayers.numLoadingLayers > 0); + statusWriter.setLoading(factoryProduct.numLoadingLayers > 0); - for (const message of viewsAndLayers.errorMessages) { + for (const message of factoryProduct.errorMessages) { statusWriter.addError(message); } diff --git a/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation.ts b/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation.ts index 593f2338e..5b8b413eb 100644 --- a/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation.ts +++ b/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation.ts @@ -16,9 +16,9 @@ import type { MakeSettingTypesMap, Settings } from "../settings/settingsDefiniti export type DataLayerInformationAccessors< TSettings extends Settings, TData, - TStoredData extends StoredData = Record, + TStoredData extends StoredData = Record, TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, - TSettingTypes extends MakeSettingTypesMap = MakeSettingTypesMap + TSettingTypes extends MakeSettingTypesMap = MakeSettingTypesMap, > = { /** * Access the data that the layer is currently storing. @@ -96,7 +96,7 @@ export type DataLayerInformationAccessors< export type FetchDataParams< TSettings extends Settings, TData, - TStoredData extends StoredData = Record + TStoredData extends StoredData = Record, > = { queryClient: QueryClient; registerQueryKey: (key: unknown[]) => void; @@ -108,7 +108,7 @@ export interface CustomDataLayerImplementation< TStoredData extends StoredData = Record, TSettingTypes extends MakeSettingTypesMap = MakeSettingTypesMap, TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, - TStoredDataKey extends keyof TStoredData = keyof TStoredData + TStoredDataKey extends keyof TStoredData = keyof TStoredData, > extends CustomSettingsHandler { /** * The default name of a layer of this type. @@ -126,7 +126,7 @@ export interface CustomDataLayerImplementation< doSettingsChangesRequireDataRefetch( prevSettings: TSettingTypes | null, newSettings: TSettingTypes, - accessors: DataLayerInformationAccessors + accessors: DataLayerInformationAccessors, ): boolean; /** diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer.ts index 1ed94e440..9459744a2 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer.ts @@ -31,8 +31,10 @@ const intersectionRealizationGridSettings = [ Setting.GRID_NAME, Setting.TIME_OR_INTERVAL, Setting.SHOW_GRID_LINES, + Setting.COLOR_SCALE, + Setting.SHOW_GRID_LINES, ] as const; -type IntersectionRealizationGridSettings = typeof intersectionRealizationGridSettings; +export type IntersectionRealizationGridSettings = typeof intersectionRealizationGridSettings; type SettingsWithTypes = MakeSettingTypesMap; export type IntersectionRealizationGridData = PolylineIntersection_trans; @@ -63,9 +65,11 @@ export class IntersectionRealizationGridLayer makeValueRange({ getData, - }: DataLayerInformationAccessors): - | [number, number] - | null { + }: DataLayerInformationAccessors< + IntersectionRealizationGridSettings, + IntersectionRealizationGridData, + StoredData + >): [number, number] | null { const data = getData(); if (!data) { return null; @@ -80,7 +84,11 @@ export class IntersectionRealizationGridLayer areCurrentSettingsValid({ getSetting, - }: DataLayerInformationAccessors): boolean { + }: DataLayerInformationAccessors< + IntersectionRealizationGridSettings, + IntersectionRealizationGridData, + StoredData + >): boolean { return ( getSetting(Setting.INTERSECTION) !== null && getSetting(Setting.ENSEMBLE) !== null && @@ -240,8 +248,8 @@ export class IntersectionRealizationGridLayer new Set( gridAttributeArr .filter((attr) => attr.property_name === gridAttribute) - .map((gridAttribute) => gridAttribute.iso_date_or_interval ?? "NO_TIME") - ) + .map((gridAttribute) => gridAttribute.iso_date_or_interval ?? "NO_TIME"), + ), ), ]; @@ -315,15 +323,15 @@ export class IntersectionRealizationGridLayer ...calcExtendedSimplifiedWellboreTrajectoryInXYPlane( path, 0, - 5 - ).simplifiedWellboreTrajectoryXy.flat() + 5, + ).simplifiedWellboreTrajectoryXy.flat(), ); resolve(polylineUtmXy); }); } else { const intersectionPolyline = getGlobalSetting("intersectionPolylines").find( - (polyline) => polyline.id === intersection.uuid + (polyline) => polyline.id === intersection.uuid, ); if (!intersectionPolyline) { resolve([]); @@ -354,7 +362,7 @@ export class IntersectionRealizationGridLayer }, body: { polyline_utm_xy }, }), - }) + }), ) .then(transformPolylineIntersection); diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/ObservedSurfaceLayer.ts similarity index 100% rename from frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/ObservedSurfaceLayer.ts rename to frontend/src/modules/_shared/LayerFramework/layers/implementations/ObservedSurfaceLayer.ts diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer.ts similarity index 83% rename from frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer.ts rename to frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer.ts index 229ff54e4..d3a40fb52 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationGridLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer.ts @@ -17,7 +17,9 @@ const realizationGridSettings = [ Setting.REALIZATION, Setting.ATTRIBUTE, Setting.GRID_NAME, - Setting.GRID_LAYER_K, + Setting.GRID_LAYER_I_RANGE, + Setting.GRID_LAYER_J_RANGE, + Setting.GRID_LAYER_K_RANGE, Setting.TIME_OR_INTERVAL, Setting.SHOW_GRID_LINES, Setting.COLOR_SCALE, @@ -30,16 +32,8 @@ export type RealizationGridData = { gridParameterData: GridMappedProperty_trans; }; -type StoredData = { - availableGridDimensions: { - i: number; - j: number; - k: number; - }; -}; - export class RealizationGridLayer - implements CustomDataLayerImplementation + implements CustomDataLayerImplementation { settings = realizationGridSettings; @@ -73,7 +67,7 @@ export class RealizationGridLayer getStoredData, registerQueryKey, queryClient, - }: FetchDataParams): Promise<{ + }: FetchDataParams): Promise<{ gridSurfaceData: GridSurface_trans; gridParameterData: GridMappedProperty_trans; }> { @@ -85,14 +79,9 @@ export class RealizationGridLayer if (timeOrInterval === "NO_TIME") { timeOrInterval = null; } - const availableDimensions = getStoredData("availableGridDimensions"); - const layerIndex = getSetting(Setting.GRID_LAYER_K); - const iMin = 0; - const iMax = availableDimensions?.i ?? 0; - const jMin = 0; - const jMax = availableDimensions?.j ?? 0; - const kMin = layerIndex || 0; - const kMax = layerIndex || 0; + const [iMin, iMax] = getSetting(Setting.GRID_LAYER_I_RANGE) ?? [0, 0]; + const [jMin, jMax] = getSetting(Setting.GRID_LAYER_J_RANGE) ?? [0, 0]; + const [kMin, kMax] = getSetting(Setting.GRID_LAYER_K_RANGE) ?? [0, 0]; const queryKey = [ "gridParameter", ensembleIdent, @@ -163,7 +152,9 @@ export class RealizationGridLayer getSetting(Setting.REALIZATION) !== null && getSetting(Setting.GRID_NAME) !== null && getSetting(Setting.ATTRIBUTE) !== null && - getSetting(Setting.GRID_LAYER_K) !== null && + getSetting(Setting.GRID_LAYER_K_RANGE) !== null && + getSetting(Setting.GRID_LAYER_I_RANGE) !== null && + getSetting(Setting.GRID_LAYER_J_RANGE) !== null && getSetting(Setting.TIME_OR_INTERVAL) !== null ); } @@ -173,7 +164,7 @@ export class RealizationGridLayer availableSettingsUpdater, storedDataUpdater, queryClient, - }: DefineDependenciesArgs) { + }: DefineDependenciesArgs) { availableSettingsUpdater(Setting.ENSEMBLE, ({ getGlobalSetting }) => { const fieldIdentifier = getGlobalSetting("fieldId"); const ensembles = getGlobalSetting("ensembles"); @@ -247,7 +238,41 @@ export class RealizationGridLayer return availableGridAttributes; }); - availableSettingsUpdater(Setting.GRID_LAYER_K, ({ getLocalSetting, getHelperDependency }) => { + availableSettingsUpdater(Setting.GRID_LAYER_I_RANGE, ({ getLocalSetting, getHelperDependency }) => { + const gridName = getLocalSetting(Setting.GRID_NAME); + const data = getHelperDependency(realizationGridDataDep); + + if (!gridName || !data) { + return [0, 0]; + } + + const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; + const availableGridLayers: [number, number] = [0, 0]; + if (gridDimensions) { + availableGridLayers[1] = gridDimensions.i_count; + } + + return availableGridLayers; + }); + + availableSettingsUpdater(Setting.GRID_LAYER_J_RANGE, ({ getLocalSetting, getHelperDependency }) => { + const gridName = getLocalSetting(Setting.GRID_NAME); + const data = getHelperDependency(realizationGridDataDep); + + if (!gridName || !data) { + return [0, 0]; + } + + const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; + const availableGridLayers: [number, number] = [0, 0]; + if (gridDimensions) { + availableGridLayers[1] = gridDimensions.j_count; + } + + return availableGridLayers; + }); + + availableSettingsUpdater(Setting.GRID_LAYER_K_RANGE, ({ getLocalSetting, getHelperDependency }) => { const gridName = getLocalSetting(Setting.GRID_NAME); const data = getHelperDependency(realizationGridDataDep); @@ -288,25 +313,5 @@ export class RealizationGridLayer return availableTimeOrIntervals; }); - - storedDataUpdater("availableGridDimensions", ({ getHelperDependency }) => { - const data = getHelperDependency(realizationGridDataDep); - - if (!data) { - return { - i: 0, - j: 0, - k: 0, - }; - } - - const gridDimensions = data[0].dimensions; - - return { - i: gridDimensions.i_count, - j: gridDimensions.j_count, - k: gridDimensions.k_count, - }; - }); } } diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationPolygonsLayer.ts similarity index 100% rename from frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationPolygonsLayer.ts rename to frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationPolygonsLayer.ts diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer.ts similarity index 97% rename from frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer.ts rename to frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer.ts index e21bae1cc..ddb4d9b58 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/RealizationSurfaceLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer.ts @@ -57,7 +57,7 @@ export class RealizationSurfaceLayer ...prevSettings, colorScale: null, }, - { ...newSettings, colorScale: null } + { ...newSettings, colorScale: null }, ); } @@ -162,8 +162,8 @@ export class RealizationSurfaceLayer const availableSurfaceNames = [ ...Array.from( new Set( - data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name) - ) + data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name), + ), ), ]; @@ -185,8 +185,8 @@ export class RealizationSurfaceLayer new Set( data.surfaces .filter((surface) => surface.attribute_name === attribute && surface.name === surfaceName) - .map((el) => el.time_type) - ) + .map((el) => el.time_type), + ), ), ]; diff --git a/frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer.ts similarity index 100% rename from frontend/src/modules/2DViewer/LayerFramework/customLayerImplementations/StatisticalSurfaceLayer.ts rename to frontend/src/modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer.ts diff --git a/frontend/src/modules/_shared/LayerFramework/layers/layerTypes.ts b/frontend/src/modules/_shared/LayerFramework/layers/layerTypes.ts index 62282cbbb..170ea7e25 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/layerTypes.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/layerTypes.ts @@ -1,4 +1,13 @@ export enum LayerType { DRILLED_WELLBORE_PICKS = "DRILLED_WELLBORE_PICKS", DRILLED_WELL_TRAJECTORIES = "DRILLED_WELL_TRAJECTORIES", + REALIZATION_GRID = "REALIZATION_GRID", + REALIZATION_SURFACE = "REALIZATION_SURFACE", + REALIZATION_POLYGONS = "REALIZATION_POLYGONS", + REALIZATION_SEISMIC_DEPTH_SLICE = "REALIZATION_SEISMIC_DEPTH_SLICE", + REALIZATION_SEISMIC_INLINE = "REALIZATION_SEISMIC_INLINE", + REALIZATION_SEISMIC_CROSSLINE = "REALIZATION_SEISMIC_CROSSLINE", + OBSERVED_SURFACE = "OBSERVED_SURFACE", + INTERSECTION_REALIZATION_GRID = "INTERSECTION_REALIZATION_GRID", + STATISTICAL_SURFACE = "STATISTICAL_SURFACE", } diff --git a/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts b/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts index ea66a162d..ddfedeac0 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts @@ -1,7 +1,26 @@ +import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; +import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer"; +import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; + import { LayerRegistry } from "./LayerRegistry"; import { DrilledWellTrajectoriesLayer } from "./implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "./implementations/DrilledWellborePicksLayer"; +import { IntersectionRealizationGridLayer } from "./implementations/IntersectionRealizationGridLayer"; +import { ObservedSurfaceLayer } from "./implementations/ObservedSurfaceLayer"; +import { RealizationGridLayer } from "./implementations/RealizationGridLayer"; +import { RealizationPolygonsLayer } from "./implementations/RealizationPolygonsLayer"; +import { RealizationSurfaceLayer } from "./implementations/RealizationSurfaceLayer"; +import { StatisticalSurfaceLayer } from "./implementations/StatisticalSurfaceLayer"; import { LayerType } from "./layerTypes"; LayerRegistry.registerLayer(LayerType.DRILLED_WELLBORE_PICKS, DrilledWellborePicksLayer); LayerRegistry.registerLayer(LayerType.DRILLED_WELL_TRAJECTORIES, DrilledWellTrajectoriesLayer); +LayerRegistry.registerLayer(LayerType.INTERSECTION_REALIZATION_GRID, IntersectionRealizationGridLayer); +LayerRegistry.registerLayer(LayerType.REALIZATION_SURFACE, RealizationSurfaceLayer); +LayerRegistry.registerLayer(LayerType.REALIZATION_POLYGONS, RealizationPolygonsLayer); +LayerRegistry.registerLayer(LayerType.REALIZATION_SEISMIC_DEPTH_SLICE, RealizationSeismicDepthSliceLayer); +LayerRegistry.registerLayer(LayerType.REALIZATION_SEISMIC_INLINE, RealizationSeismicInlineLayer); +LayerRegistry.registerLayer(LayerType.REALIZATION_SEISMIC_CROSSLINE, RealizationSeismicCrosslineLayer); +LayerRegistry.registerLayer(LayerType.OBSERVED_SURFACE, ObservedSurfaceLayer); +LayerRegistry.registerLayer(LayerType.STATISTICAL_SURFACE, StatisticalSurfaceLayer); +LayerRegistry.registerLayer(LayerType.REALIZATION_GRID, RealizationGridLayer); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting.tsx deleted file mode 100644 index 0a9848ac5..000000000 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerIRangeSetting.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React from "react"; - -import { Slider } from "@lib/components/Slider"; - -import { SettingDelegate } from "../../delegates/SettingDelegate"; -import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; -import { SettingRegistry } from "../SettingRegistry"; -import { SettingType } from "../settingsTypes"; - -type ValueType = [number, number] | null; - -export class GridLayerIRangeSetting implements Setting { - private _delegate: SettingDelegate; - - constructor() { - this._delegate = new SettingDelegate(null, this); - } - - getType(): SettingType { - return SettingType.GRID_LAYER_I_RANGE; - } - - getLabel(): string { - return "Grid layer I"; - } - - getDelegate(): SettingDelegate { - return this._delegate; - } - - isValueValid(availableValues: AvailableValuesType, value: ValueType): boolean { - if (value === null) { - return false; - } - - if (availableValues.length < 3) { - return false; - } - - const min = 0; - const max = availableValues[0]; - - if (max === null) { - return false; - } - - return value[0] >= min && value[0] <= max; - } - - fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { - if (availableValues.length < 3) { - return null; - } - - const min = 0; - const max = availableValues[0]; - - if (max === null) { - return null; - } - - if (currentValue === null) { - return [min, max]; - } - - return [Math.max(currentValue[0], min), Math.min(currentValue[1], max)]; - } - - makeComponent(): (props: SettingComponentProps) => React.ReactNode { - return function IRangeSlider(props: SettingComponentProps) { - function handleChange(_: any, value: number | number[]) { - if (!Array.isArray(value)) { - return; - } - - props.onValueChange([value[0], value[1]]); - } - - return ( - - ); - }; - } -} - -SettingRegistry.registerSetting(GridLayerIRangeSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting.tsx deleted file mode 100644 index f6804db02..000000000 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerJRangeSetting.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React from "react"; - -import { Slider } from "@lib/components/Slider"; - -import { SettingDelegate } from "../../delegates/SettingDelegate"; -import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; -import { SettingRegistry } from "../SettingRegistry"; -import { SettingType } from "../settingsTypes"; - -type ValueType = [number, number] | null; - -export class GridLayerJRangeSetting implements Setting { - private _delegate: SettingDelegate; - - constructor() { - this._delegate = new SettingDelegate(null, this); - } - - getType(): SettingType { - return SettingType.GRID_LAYER_J_RANGE; - } - - getLabel(): string { - return "Grid layer J"; - } - - getDelegate(): SettingDelegate { - return this._delegate; - } - - isValueValid(availableValues: AvailableValuesType, value: ValueType): boolean { - if (value === null) { - return false; - } - - if (availableValues.length < 3) { - return false; - } - - const min = 0; - const max = availableValues[1]; - - if (max === null) { - return false; - } - - return value[0] >= min && value[0] <= max; - } - - fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { - if (availableValues.length < 3) { - return null; - } - - const min = 0; - const max = availableValues[1]; - - if (max === null) { - return null; - } - - if (currentValue === null) { - return [min, max]; - } - - return [Math.max(currentValue[0], min), Math.min(currentValue[1], max)]; - } - - makeComponent(): (props: SettingComponentProps) => React.ReactNode { - return function JRangeSlider(props: SettingComponentProps) { - function handleChange(_: any, value: number | number[]) { - if (!Array.isArray(value)) { - return; - } - - props.onValueChange([value[0], value[1]]); - } - - return ( - - ); - }; - } -} - -SettingRegistry.registerSetting(GridLayerJRangeSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting.tsx deleted file mode 100644 index bd2ac0aaa..000000000 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerKRangeSetting.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React from "react"; - -import { Slider } from "@lib/components/Slider"; - -import { SettingDelegate } from "../../delegates/SettingDelegate"; -import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; -import { SettingRegistry } from "../SettingRegistry"; -import { SettingType } from "../settingsTypes"; - -type ValueType = [number, number] | null; - -export class GridLayerKRangeSetting implements Setting { - private _delegate: SettingDelegate; - - constructor() { - this._delegate = new SettingDelegate(null, this); - } - - getType(): SettingType { - return SettingType.GRID_LAYER_K_RANGE; - } - - getLabel(): string { - return "Grid layer K"; - } - - getDelegate(): SettingDelegate { - return this._delegate; - } - - isValueValid(availableValues: AvailableValuesType, value: ValueType): boolean { - if (value === null) { - return false; - } - - if (availableValues.length < 3) { - return false; - } - - const min = 0; - const max = availableValues[2]; - - if (max === null) { - return false; - } - - return value[0] >= min && value[0] <= max; - } - - fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { - if (availableValues.length < 3) { - return null; - } - - const min = 0; - const max = availableValues[2]; - - if (max === null) { - return null; - } - - if (currentValue === null) { - return [min, max]; - } - - return [Math.max(currentValue[0], min), Math.min(currentValue[1], max)]; - } - - makeComponent(): (props: SettingComponentProps) => React.ReactNode { - return function KRangeSlider(props: SettingComponentProps) { - function handleChange(_: any, value: number | number[]) { - if (!Array.isArray(value)) { - return; - } - - props.onValueChange([value[0], value[1]]); - } - - return ( - - ); - }; - } -} - -SettingRegistry.registerSetting(GridLayerKRangeSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx deleted file mode 100644 index 182af9411..000000000 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicCrosslineSetting.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import React from "react"; - -import { Input } from "@lib/components/Input"; -import { Slider } from "@lib/components/Slider"; - -import { SettingDelegate } from "../../delegates/SettingDelegate"; -import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; -import { SettingRegistry } from "../SettingRegistry"; -import { SettingType } from "../settingsTypes"; - -type ValueType = number | null; - -export class SeismicCrosslineSetting implements Setting { - private _delegate: SettingDelegate; - - constructor() { - this._delegate = new SettingDelegate(null, this); - } - - getType(): SettingType { - return SettingType.SEISMIC_CROSSLINE; - } - - getLabel(): string { - return "Seismic crossline"; - } - - getDelegate(): SettingDelegate { - return this._delegate; - } - - isValueValid(availableValues: AvailableValuesType, value: ValueType): boolean { - if (value === null) { - return false; - } - - if (availableValues.length < 2) { - return false; - } - - const min = 0; - const max = availableValues[1]; - - if (max === null) { - return false; - } - - return value >= min && value <= max; - } - - fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { - if (availableValues.length < 2) { - return null; - } - - const min = availableValues[0]; - const max = availableValues[1]; - - if (max === null) { - return null; - } - - if (currentValue === null) { - return min; - } - - return Math.min(Math.max(currentValue, min), max); - } - - makeComponent(): (props: SettingComponentProps) => React.ReactNode { - return function KRangeSlider(props: SettingComponentProps) { - function handleSliderChange(_: any, value: number | number[]) { - if (Array.isArray(value)) { - return value[0]; - } - - props.onValueChange(value); - } - function handleInputChange(event: React.ChangeEvent) { - props.onValueChange(Number(event.target.value)); - } - - const validValue = props.value ?? props.availableValues[0] ?? 1; - - return ( -
-
- -
-
- -
-
- ); - }; - } -} - -SettingRegistry.registerSetting(SeismicCrosslineSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx deleted file mode 100644 index 14f66af1f..000000000 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicInlineSetting.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import React from "react"; - -import { Input } from "@lib/components/Input"; -import { Slider } from "@lib/components/Slider"; - -import { SettingDelegate } from "../../delegates/SettingDelegate"; -import { AvailableValuesType, Setting, SettingComponentProps } from "../../interfaces"; -import { SettingRegistry } from "../SettingRegistry"; -import { SettingType } from "../settingsTypes"; - -type ValueType = number | null; - -export class SeismicInlineSetting implements Setting { - private _delegate: SettingDelegate; - - constructor() { - this._delegate = new SettingDelegate(null, this); - } - - getType(): SettingType { - return SettingType.SEISMIC_INLINE; - } - - getLabel(): string { - return "Seismic inline"; - } - - getDelegate(): SettingDelegate { - return this._delegate; - } - - isValueValid(availableValues: AvailableValuesType, value: ValueType): boolean { - if (value === null) { - return false; - } - - if (availableValues.length < 2) { - return false; - } - - const min = 0; - const max = availableValues[1]; - - if (max === null) { - return false; - } - - return value >= min && value <= max; - } - - fixupValue(availableValues: AvailableValuesType, currentValue: ValueType): ValueType { - if (availableValues.length < 2) { - return null; - } - - const min = availableValues[0]; - const max = availableValues[1]; - - if (max === null) { - return null; - } - - if (currentValue === null) { - return min; - } - - return Math.min(Math.max(currentValue, min), max); - } - - makeComponent(): (props: SettingComponentProps) => React.ReactNode { - return function KRangeSlider(props: SettingComponentProps) { - function handleSliderChange(_: any, value: number | number[]) { - if (Array.isArray(value)) { - return value[0]; - } - - props.onValueChange(value); - } - function handleInputChange(event: React.ChangeEvent) { - props.onValueChange(Number(event.target.value)); - } - - const validValue = props.value ?? props.availableValues[0] ?? 1; - - return ( -
-
- -
-
- -
-
- ); - }; - } -} - -SettingRegistry.registerSetting(SeismicInlineSetting); diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts index 102067cda..5e3db582a 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts @@ -15,6 +15,7 @@ import type { DataLayerInformationAccessors, } from "../interfacesAndTypes/customDataLayerImplementation"; import { instanceofItemGroup } from "../interfacesAndTypes/entitites"; +import type { StoredData } from "../interfacesAndTypes/sharedTypes"; import type { Settings } from "../settings/settingsDefinitions"; export enum VisualizationTarget { @@ -26,8 +27,9 @@ export enum VisualizationTarget { export type FactoryFunctionArgs< TSettings extends Settings, TData, - TInjectedData extends Record = never -> = DataLayerInformationAccessors & { + TStoredData extends StoredData = Record, + TInjectedData extends Record = never, +> = DataLayerInformationAccessors & { id: string; name: string; isLoading: boolean; @@ -45,51 +47,74 @@ export type LayerVisualizationFunctions< TSettings extends Settings, TData, TTarget extends VisualizationTarget, + TStoredData extends StoredData = Record, TInjectedData extends Record = never, - TAccumulatedData extends Record = never + TAccumulatedData extends Record = never, > = { - makeVisualizationFunction: MakeVisualizationFunction; - calculateBoundingBoxFunction?: CalculateBoundingBoxFunction; - makeAnnotationsFunction?: MakeAnnotationsFunction; - makeHoverVisualizationFunction?: MakeHoverVisualizationFunction; - reduceAccumulatedDataFunction?: ReduceAccumulatedDataFunction; + makeVisualizationFunction: MakeVisualizationFunction; + calculateBoundingBoxFunction?: CalculateBoundingBoxFunction; + makeAnnotationsFunction?: MakeAnnotationsFunction; + makeHoverVisualizationFunction?: MakeHoverVisualizationFunction< + TSettings, + TData, + TTarget, + TStoredData, + TInjectedData + >; + reduceAccumulatedDataFunction?: ReduceAccumulatedDataFunction< + TSettings, + TData, + TAccumulatedData, + TStoredData, + TInjectedData + >; }; export type MakeVisualizationFunction< TSettings extends Settings, TData, TTarget extends VisualizationTarget, - TInjectedData extends Record = never -> = (args: FactoryFunctionArgs) => TargetReturnTypes[TTarget] | null; + TStoredData extends StoredData = Record, + TInjectedData extends Record = never, +> = (args: FactoryFunctionArgs) => TargetReturnTypes[TTarget] | null; // This does likely require a refactor as soon as we have tested against a use case export type MakeHoverVisualizationFunction< TSettings extends Settings, TData, TTarget extends VisualizationTarget, - TInjectedData extends Record = never + TStoredData extends StoredData = Record, + TInjectedData extends Record = never, > = ( - args: FactoryFunctionArgs & { hoverInfo: Partial } + args: FactoryFunctionArgs & { + hoverInfo: Partial; + }, ) => TargetReturnTypes[TTarget][]; export type CalculateBoundingBoxFunction< TSettings extends Settings, TData, - TInjectedData extends Record = never -> = (args: FactoryFunctionArgs) => bbox.BBox | null; + TStoredData extends StoredData = Record, + TInjectedData extends Record = never, +> = (args: FactoryFunctionArgs) => bbox.BBox | null; export type MakeAnnotationsFunction< TSettings extends Settings, TData, - TInjectedData extends Record = never -> = (args: FactoryFunctionArgs) => Annotation[]; + TStoredData extends StoredData = Record, + TInjectedData extends Record = never, +> = (args: FactoryFunctionArgs) => Annotation[]; export type ReduceAccumulatedDataFunction< TSettings extends Settings, TData, TAccumulatedData, - TInjectedData extends Record = never -> = (accumulatedData: TAccumulatedData, args: FactoryFunctionArgs) => TAccumulatedData; + TStoredData extends StoredData = Record, + TInjectedData extends Record = never, +> = ( + accumulatedData: TAccumulatedData, + args: FactoryFunctionArgs, +) => TAccumulatedData; export type LayerWithPosition = { layer: TargetReturnTypes[TTarget]; @@ -106,7 +131,7 @@ export type VisualizationView = { export type FactoryProduct< TTarget extends VisualizationTarget, - TAccumulatedData extends Record = never + TAccumulatedData extends Record = never, > = { views: VisualizationView[]; layers: LayerWithPosition[]; @@ -121,19 +146,19 @@ export type FactoryProduct< export class VisualizationFactory< TTarget extends VisualizationTarget, TInjectedData extends Record = never, - TAccumulatedData extends Record = never + TAccumulatedData extends Record = never, > { private _visualizationFunctions: Map< string, - LayerVisualizationFunctions + LayerVisualizationFunctions > = new Map(); - registerLayerFunctions( + registerLayerFunctions>( layerName: string, layerCtor: { - new (...params: any[]): CustomDataLayerImplementation; + new (...params: any[]): CustomDataLayerImplementation; }, - funcs: LayerVisualizationFunctions + funcs: LayerVisualizationFunctions, ): void { if (this._visualizationFunctions.has(layerCtor.name)) { throw new Error(`Visualization function for layer ${layerCtor.name} already registered`); @@ -146,12 +171,12 @@ export class VisualizationFactory< options?: { injectedData?: TInjectedData; initialAccumulatedData?: TAccumulatedData; - } + }, ): FactoryProduct { return this.makeRecursively( layerManager.getGroupDelegate(), options?.initialAccumulatedData ?? ({} as TAccumulatedData), - options?.injectedData + options?.injectedData, ); } @@ -159,14 +184,14 @@ export class VisualizationFactory< groupDelegate: GroupDelegate, accumulatedData: TAccumulatedData, injectedData?: TInjectedData, - numCollectedLayers: number = 0 + numCollectedLayers: number = 0, ): FactoryProduct { const collectedViews: VisualizationView[] = []; const collectedLayers: LayerWithPosition[] = []; const collectedAnnotations: Annotation[] = []; const collectedErrorMessages: (StatusMessage | string)[] = []; const collectedMakeHoverVisualizationFunctions: (( - hoverInfo: Partial + hoverInfo: Partial, ) => TargetReturnTypes[TTarget][])[] = []; let collectedNumLoadingLayers = 0; let globalBoundingBox: bbox.BBox | null = null; @@ -199,7 +224,7 @@ export class VisualizationFactory< child.getGroupDelegate(), accumulatedData, injectedData, - numCollectedLayers + collectedLayers.length + numCollectedLayers + collectedLayers.length, ); accumulatedData = newAccumulatedData; @@ -276,10 +301,14 @@ export class VisualizationFactory< }; } - private makeFactoryFunctionArgs( + private makeFactoryFunctionArgs< + TSettings extends Settings, + TData, + TStoredData extends StoredData = Record, + >( layer: DataLayer, - injectedData?: TInjectedData - ): FactoryFunctionArgs { + injectedData?: TInjectedData, + ): FactoryFunctionArgs { function getInjectedData() { if (!injectedData) { throw new Error("No injected data provided. Did you forget to pass it to the factory?"); @@ -298,7 +327,7 @@ export class VisualizationFactory< private makeLayer( layer: DataLayer, - injectedData?: TInjectedData + injectedData?: TInjectedData, ): TargetReturnTypes[TTarget] | null { const func = this._visualizationFunctions.get(layer.getType())?.makeVisualizationFunction; if (!func) { @@ -310,7 +339,7 @@ export class VisualizationFactory< private makeHoverLayerFunction( layer: DataLayer, - injectedData?: TInjectedData + injectedData?: TInjectedData, ): (hoverInfo: Partial) => TargetReturnTypes[TTarget][] { const func = this._visualizationFunctions.get(layer.getType())?.makeHoverVisualizationFunction; if (!func) { @@ -343,7 +372,7 @@ export class VisualizationFactory< private accumulateLayerData( layer: DataLayer, accumulatedData: TAccumulatedData, - injectedData?: TInjectedData + injectedData?: TInjectedData, ): TAccumulatedData | null { const func = this._visualizationFunctions.get(layer.getType())?.reduceAccumulatedDataFunction; if (!func) { diff --git a/frontend/src/modules/2DViewer/LayerFramework/boundingBoxes/makePolygonDataBoundingBox.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makePolygonDataBoundingBox.ts similarity index 100% rename from frontend/src/modules/2DViewer/LayerFramework/boundingBoxes/makePolygonDataBoundingBox.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makePolygonDataBoundingBox.ts diff --git a/frontend/src/modules/2DViewer/LayerFramework/boundingBoxes/makeRealizationGridBoundingBox.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationGridBoundingBox.ts similarity index 89% rename from frontend/src/modules/2DViewer/LayerFramework/boundingBoxes/makeRealizationGridBoundingBox.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationGridBoundingBox.ts index 96d0e335f..a4d19fe80 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/boundingBoxes/makeRealizationGridBoundingBox.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationGridBoundingBox.ts @@ -1,7 +1,7 @@ import type { BBox } from "@lib/utils/bbox"; import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import type { RealizationGridData } from "../customLayerImplementations/RealizationGridLayer"; +import type { RealizationGridData } from "../../../layers/implementations/RealizationGridLayer"; export function makeRealizationGridBoundingBox({ getData, diff --git a/frontend/src/modules/2DViewer/LayerFramework/boundingBoxes/makeSurfaceLayerBoundingBox.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeSurfaceLayerBoundingBox.ts similarity index 87% rename from frontend/src/modules/2DViewer/LayerFramework/boundingBoxes/makeSurfaceLayerBoundingBox.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeSurfaceLayerBoundingBox.ts index 4b329b1d5..321ad94b0 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/boundingBoxes/makeSurfaceLayerBoundingBox.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeSurfaceLayerBoundingBox.ts @@ -1,7 +1,7 @@ import type { BBox } from "@lib/utils/bbox"; import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import type { RealizationSurfaceData } from "../customLayerImplementations/RealizationSurfaceLayer"; +import type { RealizationSurfaceData } from "../../../layers/implementations/RealizationSurfaceLayer"; export function makeSurfaceLayerBoundingBox({ getData, diff --git a/frontend/src/modules/2DViewer/LayerFramework/visualization/makeObservedSurfaceLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeObservedSurfaceLayer.ts similarity index 95% rename from frontend/src/modules/2DViewer/LayerFramework/visualization/makeObservedSurfaceLayer.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeObservedSurfaceLayer.ts index 8a849d97b..268aa7bb2 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/visualization/makeObservedSurfaceLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeObservedSurfaceLayer.ts @@ -11,7 +11,7 @@ import { type ObservedSurfaceData, type ObservedSurfaceSettings, SurfaceDataFormat, -} from "../customLayerImplementations/ObservedSurfaceLayer"; +} from "../../layers/implementations/ObservedSurfaceLayer"; function calcBoundsForRotationAroundUpperLeftCorner(surfDef: SurfaceDef_api): [number, number, number, number] { const width = (surfDef.npoints_x - 1) * surfDef.inc_x; @@ -54,7 +54,7 @@ export function makeObservedSurfaceLayer({ colorMapFunction: makeColorMapFunctionFromColorScale( colorScale, data.surfaceData.value_min, - data.surfaceData.value_max + data.surfaceData.value_max, ), }); } @@ -74,7 +74,7 @@ export function makeObservedSurfaceLayer({ colorMapFunction: makeColorMapFunctionFromColorScale( colorScale, data.surfaceData.value_min, - data.surfaceData.value_max + data.surfaceData.value_max, ), }); } diff --git a/frontend/src/modules/2DViewer/LayerFramework/visualization/makeRealizationGridLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer.ts similarity index 94% rename from frontend/src/modules/2DViewer/LayerFramework/visualization/makeRealizationGridLayer.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer.ts index 77361b668..576391834 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/visualization/makeRealizationGridLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer.ts @@ -3,7 +3,7 @@ import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visual import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; import { Grid3DLayer } from "@webviz/subsurface-viewer/dist/layers"; -import type { RealizationGridData, RealizationGridSettings } from "../customLayerImplementations/RealizationGridLayer"; +import type { RealizationGridData, RealizationGridSettings } from "../../layers/implementations/RealizationGridLayer"; export function makeRealizationGridLayer({ id, @@ -38,7 +38,7 @@ export function makeRealizationGridLayer({ colorMapFunction: makeColorMapFunctionFromColorScale( colorScale, gridParameterData.min_grid_prop_value, - gridParameterData.max_grid_prop_value + gridParameterData.max_grid_prop_value, ), }); return grid3dLayer; diff --git a/frontend/src/modules/2DViewer/LayerFramework/visualization/makeRealizationPolygonsLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationPolygonsLayer.ts similarity index 96% rename from frontend/src/modules/2DViewer/LayerFramework/visualization/makeRealizationPolygonsLayer.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationPolygonsLayer.ts index 5c728ff7e..89025645d 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/visualization/makeRealizationPolygonsLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationPolygonsLayer.ts @@ -7,7 +7,7 @@ import type { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "ge import type { RealizationPolygonsData, RealizationPolygonsSettings, -} from "../customLayerImplementations/RealizationPolygonsLayer"; +} from "../../layers/implementations/RealizationPolygonsLayer"; function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { const coords: number[][] = []; diff --git a/frontend/src/modules/2DViewer/LayerFramework/visualization/makeRealizationSurfaceLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationSurfaceLayer.ts similarity index 95% rename from frontend/src/modules/2DViewer/LayerFramework/visualization/makeRealizationSurfaceLayer.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationSurfaceLayer.ts index a792d276a..eb64dee2d 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/visualization/makeRealizationSurfaceLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationSurfaceLayer.ts @@ -11,7 +11,7 @@ import { type RealizationSurfaceData, type RealizationSurfaceSettings, SurfaceDataFormat, -} from "../customLayerImplementations/RealizationSurfaceLayer"; +} from "../../layers/implementations/RealizationSurfaceLayer"; function calcBoundsForRotationAroundUpperLeftCorner(surfDef: SurfaceDef_api): [number, number, number, number] { const width = (surfDef.npoints_x - 1) * surfDef.inc_x; @@ -54,7 +54,7 @@ export function makeRealizationSurfaceLayer({ colorMapFunction: makeColorMapFunctionFromColorScale( colorScale, data.surfaceData.value_min, - data.surfaceData.value_max + data.surfaceData.value_max, ), }); } @@ -74,7 +74,7 @@ export function makeRealizationSurfaceLayer({ colorMapFunction: makeColorMapFunctionFromColorScale( colorScale, data.surfaceData.value_min, - data.surfaceData.value_max + data.surfaceData.value_max, ), }); } diff --git a/frontend/src/modules/2DViewer/LayerFramework/visualization/makeStatisticalSurfaceLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeStatisticalSurfaceLayer.ts similarity index 95% rename from frontend/src/modules/2DViewer/LayerFramework/visualization/makeStatisticalSurfaceLayer.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeStatisticalSurfaceLayer.ts index f554c6361..97801a6ed 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/visualization/makeStatisticalSurfaceLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeStatisticalSurfaceLayer.ts @@ -11,7 +11,7 @@ import { type StatisticalSurfaceData, type StatisticalSurfaceSettings, SurfaceDataFormat, -} from "../customLayerImplementations/StatisticalSurfaceLayer"; +} from "../../layers/implementations/StatisticalSurfaceLayer"; function calcBoundsForRotationAroundUpperLeftCorner(surfDef: SurfaceDef_api): [number, number, number, number] { const width = (surfDef.npoints_x - 1) * surfDef.inc_x; @@ -54,7 +54,7 @@ export function makeStatisticalSurfaceLayer({ colorMapFunction: makeColorMapFunctionFromColorScale( colorScale, data.surfaceData.value_min, - data.surfaceData.value_max + data.surfaceData.value_max, ), }); } @@ -74,7 +74,7 @@ export function makeStatisticalSurfaceLayer({ colorMapFunction: makeColorMapFunctionFromColorScale( colorScale, data.surfaceData.value_min, - data.surfaceData.value_max + data.surfaceData.value_max, ), }); } diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer.ts deleted file mode 100644 index 8774759b2..000000000 --- a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellborePicksLayer.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { WellborePick_api } from "@api"; -import { WellborePickLayerData, WellborePicksLayer } from "@modules/_shared/customDeckGlLayers/WellborePicksLayer"; - -import { VisualizationFunctionArgs } from "../VisualizationFactory"; - -export function makeWellborePicksLayer({ - id, - data, -}: VisualizationFunctionArgs): WellborePicksLayer { - const wellPicksData: WellborePickLayerData[] = data.map((wellborePick) => { - return { - easting: wellborePick.easting, - northing: wellborePick.northing, - wellBoreUwi: wellborePick.uniqueWellboreIdentifier, - tvdMsl: wellborePick.tvdMsl, - md: wellborePick.md, - slotName: "", - }; - }); - - return new WellborePicksLayer({ - id, - data: wellPicksData, - pickable: true, - zIncreaseDownwards: true, - }); -} diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts deleted file mode 100644 index dfaf95944..000000000 --- a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeWellsLayer.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { WellboreTrajectory_api } from "@api"; -import * as bbox from "@lib/utils/boundingBox"; -import { WellsLayer, WellsLayerData } from "@modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer"; - -import { Feature, GeoJsonProperties, GeometryCollection, LineString, Point } from "geojson"; - -import { VisualizationFunctionArgs } from "../VisualizationFactory"; - -function wellTrajectoryToGeojson( - wellTrajectory: WellboreTrajectory_api -): Feature { - const point: Point = { - type: "Point", - coordinates: [wellTrajectory.eastingArr[0], wellTrajectory.northingArr[0], -wellTrajectory.tvdMslArr[0]], - }; - - const coordinates: LineString = { - type: "LineString", - coordinates: zipCoords(wellTrajectory.eastingArr, wellTrajectory.northingArr, wellTrajectory.tvdMslArr), - }; - - const color = [100, 100, 100]; - const lineWidth = 2; - const wellHeadSize = 1; - - const geometryCollection: Feature = { - type: "Feature", - geometry: { - type: "GeometryCollection", - geometries: [point, coordinates], - }, - properties: { - uuid: wellTrajectory.wellboreUuid, - name: wellTrajectory.uniqueWellboreIdentifier, - uwi: wellTrajectory.uniqueWellboreIdentifier, - color, - md: [wellTrajectory.mdArr], - lineWidth, - wellHeadSize, - }, - }; - - return geometryCollection; -} - -function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { - const coords: number[][] = []; - for (let i = 0; i < xArr.length; i++) { - coords.push([xArr[i], yArr[i], -zArr[i]]); - } - - return coords; -} - -/* -export function makeWellsLayer({ - id, - data, - name, - boundingBox, -}: VisualizationFunctionArgs): AdvancedWellsLayer { - // Filter out some wellbores that are known to be not working - this is a temporary solution - const filteredData = data.filter((wellbore) => wellbore.uniqueWellboreIdentifier !== "NO 34/4-K-3 AH"); - - // WellsLayer requires data in GeoJSON format in a FeatureCollection - const featureCollection: FeatureCollection = { - type: "FeatureCollection", - features: filteredData.map((wellTrajectory) => wellTrajectoryToGeojson(wellTrajectory)), - }; - - const bbox3d = boundingBox ? bbox.toNumArray(boundingBox) : undefined; - - return new AdvancedWellsLayer({ - id, - data: featureCollection, - name, - refine: false, - wellNameVisible: false, - wellHeadStyle: { size: 1 }, - pickable: true, - ZIncreasingDownwards: false, - outline: false, - lineStyle: { - width: 2, - }, - boundingBox: bbox3d, - }); -} - */ -export function makeWellsLayer({ - id, - data, - name, - boundingBox, -}: VisualizationFunctionArgs): WellsLayer { - // Filter out some wellbores that are known to be not working - this is a temporary solution - const filteredData = data.filter((wellbore) => wellbore.uniqueWellboreIdentifier !== "NO 34/4-K-3 AH"); - - const wellsData: WellsLayerData = filteredData.map((wellTrajectory) => { - return { - coordinates: zipCoords(wellTrajectory.eastingArr, wellTrajectory.northingArr, wellTrajectory.tvdMslArr) as [ - number, - number, - number - ][], - properties: { - uuid: wellTrajectory.wellboreUuid, - name: wellTrajectory.uniqueWellboreIdentifier, - mdArray: wellTrajectory.mdArr, - }, - }; - }); - - const bbox3d = boundingBox ? bbox.toNumArray(boundingBox) : undefined; - - return new WellsLayer({ - id, - data: wellsData, - name, - boundingBox: bbox3d, - }); -} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/PlaceholderLayer.ts~HEAD b/frontend/src/modules/_shared/customDeckGlLayers/PlaceholderLayer.ts~HEAD new file mode 100644 index 000000000..b146c9fea --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/PlaceholderLayer.ts~HEAD @@ -0,0 +1,21 @@ +import { Layer } from "@deck.gl/core"; + +type PlaceholderLayerProps = { + id: string; +}; + +export class PlaceholderLayer extends Layer { + static layerName: string = "PlaceholderLayer"; + + constructor(props: PlaceholderLayerProps) { + super(props); + } + + initializeState(): void { + return; + } + + render() { + return null; + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/PlaceholderLayer.ts~main b/frontend/src/modules/_shared/customDeckGlLayers/PlaceholderLayer.ts~main new file mode 100644 index 000000000..b146c9fea --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/PlaceholderLayer.ts~main @@ -0,0 +1,21 @@ +import { Layer } from "@deck.gl/core"; + +type PlaceholderLayerProps = { + id: string; +}; + +export class PlaceholderLayer extends Layer { + static layerName: string = "PlaceholderLayer"; + + constructor(props: PlaceholderLayerProps) { + super(props); + } + + initializeState(): void { + return; + } + + render() { + return null; + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts index f699be52b..9ef1b6692 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellborePicksLayer.ts @@ -14,7 +14,7 @@ import { BoundingBox3D, ReportBoundingBoxAction } from "@webviz/subsurface-viewe import { isEqual } from "lodash"; -export type WellborePickLayerData = { +export type WellborePicksLayerData = { easting: number; northing: number; wellBoreUwi: string; @@ -25,7 +25,7 @@ export type WellborePickLayerData = { export interface WellBorePicksLayerProps extends ExtendedLayerProps { id: string; - data: WellborePickLayerData[]; + data: WellborePicksLayerData[]; zIncreaseDownwards?: boolean; // Non public properties: From 0f957ed30f0dd93aca33d372e5ab272c6b1a22fd Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 27 Mar 2025 13:21:07 +0100 Subject: [PATCH 67/97] wip --- .../layerManagerComponentWrapper.tsx | 10 +- .../view/components/LayersWrapper.tsx | 6 +- .../RealizationSeismicDepthLayer.ts | 6 +- .../layerManagerComponentWrapper.tsx | 10 +- .../view/components/LayersWrapper.tsx | 6 +- .../delegates/SettingsContextDelegate.ts | 46 ++++--- .../delegates/_utils/Dependency.ts | 17 ++- .../framework/DataLayer/DataLayer.ts | 9 ++ .../LayerFramework/framework/Group/Group.ts | 15 +-- .../framework/utils/DeserializationFactory.ts | 15 +-- .../utils/makeSortableListItemComponent.tsx | 2 +- .../customSettingsHandler.ts | 22 ++-- .../implementations/RealizationGridLayer.ts | 19 +-- .../RealizationSurfaceLayer.ts | 18 ++- .../StatisticalSurfaceLayer.ts | 30 +++-- .../LayerFramework/layers/layerTypes.ts | 8 +- .../layers/registerAllLayers.ts | 13 +- .../implementations/GridLayerRangeSetting.tsx | 45 ------- .../implementations/SeismicSliceSetting.tsx | 28 +--- .../visualization/utils/colors.ts | 6 +- .../SeismicFenceMeshLayer.ts | 124 ++++++++++++------ 21 files changed, 248 insertions(+), 207 deletions(-) diff --git a/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx index c50d3f020..70755349f 100644 --- a/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx @@ -71,13 +71,17 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper groupDelegate.appendChild(new SharedSetting(Setting.COLOR_SCALE, null, props.layerManager)); return; case "observed-surface": - groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.OBSERVED_SURFACE, props.layerManager)); + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.OBSERVED_SURFACE_2D, props.layerManager)); return; case "statistical-surface": - groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.STATISTICAL_SURFACE, props.layerManager)); + groupDelegate.prependChild( + LayerRegistry.makeLayer(LayerType.STATISTICAL_SURFACE_2D, props.layerManager), + ); return; case "realization-surface": - groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_SURFACE, props.layerManager)); + groupDelegate.prependChild( + LayerRegistry.makeLayer(LayerType.REALIZATION_SURFACE_2D, props.layerManager), + ); return; case "realization-polygons": groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_POLYGONS, props.layerManager)); diff --git a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx index fabff2fc4..8a3691806 100644 --- a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx @@ -54,17 +54,17 @@ export type LayersWrapperProps = { const VISUALIZATION_FACTORY = new VisualizationFactory(); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.OBSERVED_SURFACE, ObservedSurfaceLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.OBSERVED_SURFACE_2D, ObservedSurfaceLayer, { makeVisualizationFunction: makeObservedSurfaceLayer, calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, }); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_SURFACE, RealizationSurfaceLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_SURFACE_2D, RealizationSurfaceLayer, { makeVisualizationFunction: makeRealizationSurfaceLayer, calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, }); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.STATISTICAL_SURFACE, StatisticalSurfaceLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.STATISTICAL_SURFACE_2D, StatisticalSurfaceLayer, { makeVisualizationFunction: makeStatisticalSurfaceLayer, calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts index 2996fdc43..9e4a6898b 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts @@ -1,4 +1,4 @@ -import { type SeismicCubeMeta_api, getInlineSliceOptions, getSeismicCubeMetaListOptions } from "@api"; +import { type SeismicCubeMeta_api, getDepthSliceOptions, getSeismicCubeMetaListOptions } from "@api"; import { type SeismicSliceData_trans, transformSeismicSlice, @@ -76,7 +76,7 @@ export class RealizationSeismicDepthSliceLayer const timeOrInterval = getSetting(Setting.TIME_OR_INTERVAL); const depthSlice = getSetting(Setting.SEISMIC_DEPTH_SLICE); - const queryOptions = getInlineSliceOptions({ + const queryOptions = getDepthSliceOptions({ query: { case_uuid: ensembleIdent?.getCaseUuid() ?? "", ensemble_name: ensembleIdent?.getEnsembleName() ?? "", @@ -84,7 +84,7 @@ export class RealizationSeismicDepthSliceLayer seismic_attribute: attribute ?? "", time_or_interval_str: timeOrInterval ?? "", observed: false, - inline_no: depthSlice ?? 0, + depth_slice_no: depthSlice ?? 0, }, }); diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index 01d3003a0..3bb816bef 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -73,13 +73,17 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper groupDelegate.appendChild(new SharedSetting(Setting.COLOR_SCALE, null, props.layerManager)); return; case "observed-surface": - groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.OBSERVED_SURFACE, props.layerManager)); + groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.OBSERVED_SURFACE_2D, props.layerManager)); return; case "statistical-surface": - groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.STATISTICAL_SURFACE, props.layerManager)); + groupDelegate.prependChild( + LayerRegistry.makeLayer(LayerType.STATISTICAL_SURFACE_2D, props.layerManager), + ); return; case "realization-surface": - groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_SURFACE, props.layerManager)); + groupDelegate.prependChild( + LayerRegistry.makeLayer(LayerType.REALIZATION_SURFACE_2D, props.layerManager), + ); return; case "realization-polygons": groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_POLYGONS, props.layerManager)); diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 6664e8dd0..baddb783d 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -59,17 +59,17 @@ import { PlaceholderLayer } from "../../../_shared/customDeckGlLayers/Placeholde const VISUALIZATION_FACTORY = new VisualizationFactory(); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.OBSERVED_SURFACE, ObservedSurfaceLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.OBSERVED_SURFACE_2D, ObservedSurfaceLayer, { makeVisualizationFunction: makeObservedSurfaceLayer, calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, }); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_SURFACE, RealizationSurfaceLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_SURFACE_2D, RealizationSurfaceLayer, { makeVisualizationFunction: makeRealizationSurfaceLayer, calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, }); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.STATISTICAL_SURFACE, StatisticalSurfaceLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.STATISTICAL_SURFACE_2D, StatisticalSurfaceLayer, { makeVisualizationFunction: makeStatisticalSurfaceLayer, calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts b/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts index 2212e223d..442faf094 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/SettingsContextDelegate.ts @@ -26,11 +26,13 @@ export enum SettingsContextStatus { export enum SettingsContextDelegateTopic { SETTINGS_CHANGED = "SETTINGS_CHANGED", + STORED_DATA_CHANGED = "STORED_DATA_CHANGED", STATUS = "LOADING_STATE_CHANGED", } export type SettingsContextDelegatePayloads = { [SettingsContextDelegateTopic.SETTINGS_CHANGED]: void; + [SettingsContextDelegateTopic.STORED_DATA_CHANGED]: void; [SettingsContextDelegateTopic.STATUS]: SettingsContextStatus; }; @@ -56,7 +58,7 @@ export class SettingsContextDelegate< TSettingTypes extends MakeSettingTypesMap = MakeSettingTypesMap, TStoredData extends StoredData = Record, TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, - TStoredDataKey extends keyof TStoredData = keyof TStoredData + TStoredDataKey extends keyof TStoredData = keyof TStoredData, > implements PublishSubscribe { private _owner: DataLayer; @@ -86,7 +88,7 @@ export class SettingsContextDelegate< TStoredDataKey >, layerManager: DataLayerManager, - settings: { [K in TSettingKey]: SettingManager } + settings: { [K in TSettingKey]: SettingManager }, ) { this._owner = owner; this._customSettingsHandler = customSettingsHandler; @@ -97,13 +99,13 @@ export class SettingsContextDelegate< "settings", settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.VALUE)(() => { this.handleSettingChanged(); - }) + }), ); this._unsubscribeHandler.registerUnsubscribeFunction( "settings", settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_LOADING)(() => { this.handleSettingsLoadingStateChanged(); - }) + }), ); this._unsubscribeHandler.registerUnsubscribeFunction( "layer-manager", @@ -111,14 +113,14 @@ export class SettingsContextDelegate< .getPublishSubscribeDelegate() .makeSubscriberFunction(LayerManagerTopic.SHARED_SETTINGS_CHANGED)(() => { this.handleSharedSettingsChanged(); - }) + }), ); this._unsubscribeHandler.registerUnsubscribeFunction( "layer-manager", layerManager.getPublishSubscribeDelegate().makeSubscriberFunction(LayerManagerTopic.ITEMS)(() => { this.handleSharedSettingsChanged(); - }) + }), ); } @@ -157,11 +159,11 @@ export class SettingsContextDelegate< } const sharedSettingsProviders: SharedSettingsProvider[] = parentGroup.getAncestorAndSiblingItems( - (item) => item instanceof SharedSetting + (item) => item instanceof SharedSetting, ) as unknown as SharedSettingsProvider[]; const ancestorGroups: SharedSettingsProvider[] = parentGroup.getAncestors( - (item) => item instanceof Group && instanceofSharedSettingsProvider(item) + (item) => item instanceof Group && instanceofSharedSettingsProvider(item), ) as unknown as SharedSettingsProvider[]; sharedSettingsProviders.push(...ancestorGroups); @@ -235,6 +237,7 @@ export class SettingsContextDelegate< setStoredData(key: K, data: TStoredData[K] | null): void { this._storedData[key] = data; + this._publishSubscribeDelegate.notifySubscribers(SettingsContextDelegateTopic.STORED_DATA_CHANGED); } getSettings() { @@ -250,6 +253,9 @@ export class SettingsContextDelegate< if (topic === SettingsContextDelegateTopic.SETTINGS_CHANGED) { return; } + if (topic === SettingsContextDelegateTopic.STORED_DATA_CHANGED) { + return; + } if (topic === SettingsContextDelegateTopic.STATUS) { return this._status; } @@ -296,15 +302,15 @@ export class SettingsContextDelegate< this._unsubscribeHandler.registerUnsubscribeFunction( "dependencies", this._settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.VALUE)( - handleChange - ) + handleChange, + ), ); this._unsubscribeHandler.registerUnsubscribeFunction( "dependencies", this._settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_PERSISTED)( - handleChange - ) + handleChange, + ), ); return handleChange; @@ -312,7 +318,7 @@ export class SettingsContextDelegate< const makeGlobalSettingGetter = ( key: K, - handler: (value: GlobalSettings[K]) => void + handler: (value: GlobalSettings[K]) => void, ) => { const handleChange = (): void => { handler(this.getLayerManager.bind(this)().getGlobalSetting(key)); @@ -321,7 +327,7 @@ export class SettingsContextDelegate< "dependencies", this.getLayerManager() .getPublishSubscribeDelegate() - .makeSubscriberFunction(LayerManagerTopic.GLOBAL_SETTINGS)(handleChange) + .makeSubscriberFunction(LayerManagerTopic.GLOBAL_SETTINGS)(handleChange), ); return handleChange; @@ -329,13 +335,13 @@ export class SettingsContextDelegate< const availableSettingsUpdater = ( settingKey: K, - updateFunc: UpdateFunc, TSettings, TSettingTypes, TSettingKey> + updateFunc: UpdateFunc, TSettings, TSettingTypes, TSettingKey>, ): Dependency, TSettings, TSettingTypes, TSettingKey> => { const dependency = new Dependency, TSettings, TSettingTypes, TSettingKey>( this, updateFunc, makeLocalSettingGetter, - makeGlobalSettingGetter + makeGlobalSettingGetter, ); dependencies.push(dependency); @@ -364,7 +370,7 @@ export class SettingsContextDelegate< const storedDataUpdater = ( key: K, - updateFunc: UpdateFunc[K], TSettings, TSettingTypes, TSettingKey> + updateFunc: UpdateFunc[K], TSettings, TSettingTypes, TSettingKey>, ): Dependency[K], TSettings, TSettingTypes, TSettingKey> => { const dependency = new Dependency< NullableStoredData[K], @@ -388,16 +394,16 @@ export class SettingsContextDelegate< getLocalSetting: (settingName: T) => TSettingTypes[T]; getGlobalSetting: (settingName: T) => GlobalSettings[T]; getHelperDependency: ( - dep: Dependency + dep: Dependency, ) => TDep | null; abortSignal: AbortSignal; - }) => T + }) => T, ) => { const dependency = new Dependency( this, update, makeLocalSettingGetter, - makeGlobalSettingGetter + makeGlobalSettingGetter, ); dependencies.push(dependency); diff --git a/frontend/src/modules/_shared/LayerFramework/delegates/_utils/Dependency.ts b/frontend/src/modules/_shared/LayerFramework/delegates/_utils/Dependency.ts index bf5aafe19..468e184ff 100644 --- a/frontend/src/modules/_shared/LayerFramework/delegates/_utils/Dependency.ts +++ b/frontend/src/modules/_shared/LayerFramework/delegates/_utils/Dependency.ts @@ -4,6 +4,7 @@ import { isEqual } from "lodash"; import type { GlobalSettings } from "../../framework/DataLayerManager/DataLayerManager"; import { SettingTopic } from "../../framework/SettingManager/SettingManager"; +import { CancelUpdate } from "../../interfacesAndTypes/customSettingsHandler"; import type { UpdateFunc } from "../../interfacesAndTypes/customSettingsHandler"; import type { SettingsKeysFromTuple } from "../../interfacesAndTypes/utils"; import type { MakeSettingTypesMap, Settings } from "../../settings/settingsDefinitions"; @@ -24,7 +25,7 @@ export class Dependency< TReturnValue, TSettings extends Settings, TSettingTypes extends MakeSettingTypesMap, - TKey extends SettingsKeysFromTuple + TKey extends SettingsKeysFromTuple, > { private _updateFunc: UpdateFunc; private _dependencies: Set<(value: Awaited | null) => void> = new Set(); @@ -36,7 +37,7 @@ export class Dependency< private _makeLocalSettingGetter: (key: K, handler: (value: TSettingTypes[K]) => void) => void; private _makeGlobalSettingGetter: ( key: K, - handler: (value: GlobalSettings[K]) => void + handler: (value: GlobalSettings[K]) => void, ) => void; private _cachedSettingsMap: Map = new Map(); private _cachedGlobalSettingsMap: Map = new Map(); @@ -53,8 +54,8 @@ export class Dependency< makeLocalSettingGetter: (key: K, handler: (value: TSettingTypes[K]) => void) => void, makeGlobalSettingGetter: ( key: K, - handler: (value: GlobalSettings[K]) => void - ) => void + handler: (value: GlobalSettings[K]) => void, + ) => void, ) { this._contextDelegate = contextDelegate; this._updateFunc = updateFunc; @@ -157,7 +158,7 @@ export class Dependency< this._cachedGlobalSettingsMap.set( settingName as string, - this._contextDelegate.getLayerManager().getGlobalSetting(settingName) + this._contextDelegate.getLayerManager().getGlobalSetting(settingName), ); return this._cachedGlobalSettingsMap.get(settingName as string); } @@ -220,7 +221,7 @@ export class Dependency< this.setLoadingState(true); - let newValue: Awaited | null = null; + let newValue: Awaited | null | typeof CancelUpdate = null; try { newValue = await this._updateFunc({ getLocalSetting: this.getLocalSetting, @@ -236,6 +237,10 @@ export class Dependency< return; } + if (newValue === CancelUpdate) { + return; + } + this.applyNewValue(newValue); } diff --git a/frontend/src/modules/_shared/LayerFramework/framework/DataLayer/DataLayer.ts b/frontend/src/modules/_shared/LayerFramework/framework/DataLayer/DataLayer.ts index 2cd938b22..763db601d 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/DataLayer/DataLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/framework/DataLayer/DataLayer.ts @@ -133,6 +133,15 @@ export class DataLayer< }), ); + this._unsubscribeHandler.registerUnsubscribeFunction( + "settings-context", + this._settingsContextDelegate + .getPublishSubscribeDelegate() + .makeSubscriberFunction(SettingsContextDelegateTopic.STORED_DATA_CHANGED)(() => { + this.handleSettingsChange(); + }), + ); + this._unsubscribeHandler.registerUnsubscribeFunction( "settings-context", this._settingsContextDelegate diff --git a/frontend/src/modules/_shared/LayerFramework/framework/Group/Group.ts b/frontend/src/modules/_shared/LayerFramework/framework/Group/Group.ts index 0782df219..908c44b51 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/Group/Group.ts +++ b/frontend/src/modules/_shared/LayerFramework/framework/Group/Group.ts @@ -3,12 +3,11 @@ import { ItemDelegate } from "../../delegates/ItemDelegate"; import { SharedSettingsDelegate } from "../../delegates/SharedSettingsDelegate"; import type { CustomGroupImplementation, - CustomGroupImplementationWithSettings} from "../../interfacesAndTypes/customGroupImplementation"; -import { - includesSettings, + CustomGroupImplementationWithSettings, } from "../../interfacesAndTypes/customGroupImplementation"; +import { includesSettings } from "../../interfacesAndTypes/customGroupImplementation"; import type { ItemGroup } from "../../interfacesAndTypes/entitites"; -import type { SerializedGroup} from "../../interfacesAndTypes/serialization"; +import type { SerializedGroup } from "../../interfacesAndTypes/serialization"; import { SerializedType } from "../../interfacesAndTypes/serialization"; import type { SettingsKeysFromTuple } from "../../interfacesAndTypes/utils"; import type { MakeSettingTypesMap, SettingTypes, Settings } from "../../settings/settingsDefinitions"; @@ -18,7 +17,7 @@ import { makeSettings } from "../utils/makeSettings"; export type GroupParams< TSettingTypes extends Settings, - TSettings extends MakeSettingTypesMap = MakeSettingTypesMap + TSettings extends MakeSettingTypesMap = MakeSettingTypesMap, > = { layerManager: DataLayerManager; color?: string; @@ -31,7 +30,7 @@ export type GroupParams< export class Group< TSettings extends Settings = [], TSettingTypes extends MakeSettingTypesMap = MakeSettingTypesMap, - TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple + TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, > implements ItemGroup { private _itemDelegate: ItemDelegate; @@ -49,8 +48,8 @@ export class Group< this, makeSettings( customGroupImplementation.settings as unknown as TSettings, - customGroupImplementation.getDefaultSettingsValues() as unknown as TSettingTypes - ) + customGroupImplementation.getDefaultSettingsValues() as unknown as TSettingTypes, + ), ); } this._type = type; diff --git a/frontend/src/modules/_shared/LayerFramework/framework/utils/DeserializationFactory.ts b/frontend/src/modules/_shared/LayerFramework/framework/utils/DeserializationFactory.ts index 5a9e0291f..703992685 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/utils/DeserializationFactory.ts +++ b/frontend/src/modules/_shared/LayerFramework/framework/utils/DeserializationFactory.ts @@ -5,10 +5,9 @@ import type { SerializedItem, SerializedLayer, SerializedSettingsGroup, - SerializedSharedSetting} from "../../interfacesAndTypes/serialization"; -import { - SerializedType, + SerializedSharedSetting, } from "../../interfacesAndTypes/serialization"; +import { SerializedType } from "../../interfacesAndTypes/serialization"; import { LayerRegistry } from "../../layers/LayerRegistry"; import type { DataLayerManager } from "../DataLayerManager/DataLayerManager"; import { SettingsGroup } from "../SettingsGroup/SettingsGroup"; @@ -38,10 +37,10 @@ export class DeserializationFactory { } if (serialized.type === SerializedType.GROUP) { - const serializedView = serialized as SerializedGroup; - const view = GroupRegistry.makeGroup(serializedView.groupType, this._layerManager); - view.deserializeState(serializedView); - return view; + const serializedGroup = serialized as SerializedGroup; + const group = GroupRegistry.makeGroup(serializedGroup.groupType, this._layerManager); + group.deserializeState(serializedGroup); + return group; } if (serialized.type === SerializedType.SETTINGS_GROUP) { @@ -56,7 +55,7 @@ export class DeserializationFactory { const setting = new SharedSetting( serializedSharedSetting.wrappedSettingType, serializedSharedSetting.value, - this._layerManager + this._layerManager, ); setting.deserializeState(serializedSharedSetting); return setting; diff --git a/frontend/src/modules/_shared/LayerFramework/framework/utils/makeSortableListItemComponent.tsx b/frontend/src/modules/_shared/LayerFramework/framework/utils/makeSortableListItemComponent.tsx index 8f1143246..10de9b2b9 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/utils/makeSortableListItemComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/framework/utils/makeSortableListItemComponent.tsx @@ -14,7 +14,7 @@ import { SharedSettingComponent } from "../SharedSetting/SharedSettingComponent" export function makeSortableListItemComponent( item: Item, layerActions?: LayersActionGroup[], - onActionClick?: (identifier: string, group: ItemGroup) => void + onActionClick?: (identifier: string, group: ItemGroup) => void, ): React.ReactElement { if (item instanceof DataLayer) { return ; diff --git a/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler.ts b/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler.ts index 09da84462..dbb03ed60 100644 --- a/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler.ts +++ b/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler.ts @@ -12,23 +12,25 @@ import type { MakeSettingTypesMap, Settings } from "../settings/settingsDefiniti export interface GetHelperDependency< TSettings extends Settings, TSettingTypes extends MakeSettingTypesMap, - TKey extends SettingsKeysFromTuple + TKey extends SettingsKeysFromTuple, > { (dep: Dependency): Awaited | null; } +export const CancelUpdate = Symbol("CancelUpdate"); + export interface UpdateFunc< TReturnValue, TSettings extends Settings, TSettingTypes extends MakeSettingTypesMap, - TKey extends SettingsKeysFromTuple + TKey extends SettingsKeysFromTuple, > { (args: { getLocalSetting: (settingName: K) => TSettingTypes[K]; getGlobalSetting: (settingName: T) => GlobalSettings[T]; getHelperDependency: GetHelperDependency; abortSignal: AbortSignal; - }): TReturnValue; + }): TReturnValue | typeof CancelUpdate; } export interface DefineDependenciesArgs< @@ -36,25 +38,25 @@ export interface DefineDependenciesArgs< TStoredData extends StoredData = Record, TSettingTypes extends MakeSettingTypesMap = MakeSettingTypesMap, TKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, - TStoredDataKey extends keyof TStoredData = keyof TStoredData + TStoredDataKey extends keyof TStoredData = keyof TStoredData, > { availableSettingsUpdater: ( settingKey: TSettingKey, - update: UpdateFunc, TSettings, TSettingTypes, TKey> + update: UpdateFunc, TSettings, TSettingTypes, TKey>, ) => Dependency, TSettings, TSettingTypes, TKey>; storedDataUpdater: ( key: K, - update: UpdateFunc[TStoredDataKey], TSettings, TSettingTypes, TKey> + update: UpdateFunc[TStoredDataKey], TSettings, TSettingTypes, TKey>, ) => Dependency[TStoredDataKey], TSettings, TSettingTypes, TKey>; helperDependency: ( update: (args: { getLocalSetting: (settingName: T) => TSettingTypes[T]; getGlobalSetting: (settingName: T) => GlobalSettings[T]; getHelperDependency: ( - helperDependency: Dependency + helperDependency: Dependency, ) => TDep | null; abortSignal: AbortSignal; - }) => T + }) => T, ) => Dependency; workbenchSession: WorkbenchSession; workbenchSettings: WorkbenchSettings; @@ -70,7 +72,7 @@ export interface CustomSettingsHandler< TStoredData extends StoredData = Record, TSettingTypes extends MakeSettingTypesMap = MakeSettingTypesMap, TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, - TStoredDataKey extends keyof TStoredData = keyof TStoredData + TStoredDataKey extends keyof TStoredData = keyof TStoredData, > { /** * The settings that this handler is using/providing. @@ -135,6 +137,6 @@ export interface CustomSettingsHandler< * */ defineDependencies( - args: DefineDependenciesArgs + args: DefineDependenciesArgs, ): void; } diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer.ts index d3a40fb52..3bee3c975 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer.ts @@ -6,7 +6,10 @@ import type { DataLayerInformationAccessors, FetchDataParams, } from "@modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation"; -import type { DefineDependenciesArgs } from "@modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler"; +import { + CancelUpdate, + type DefineDependenciesArgs, +} from "@modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler"; import type { MakeSettingTypesMap } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; @@ -64,7 +67,6 @@ export class RealizationGridLayer fetchData({ getSetting, - getStoredData, registerQueryKey, queryClient, }: FetchDataParams): Promise<{ @@ -162,7 +164,6 @@ export class RealizationGridLayer defineDependencies({ helperDependency, availableSettingsUpdater, - storedDataUpdater, queryClient, }: DefineDependenciesArgs) { availableSettingsUpdater(Setting.ENSEMBLE, ({ getGlobalSetting }) => { @@ -243,7 +244,7 @@ export class RealizationGridLayer const data = getHelperDependency(realizationGridDataDep); if (!gridName || !data) { - return [0, 0]; + return CancelUpdate; } const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; @@ -260,7 +261,7 @@ export class RealizationGridLayer const data = getHelperDependency(realizationGridDataDep); if (!gridName || !data) { - return [0, 0]; + return CancelUpdate; } const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; @@ -277,7 +278,7 @@ export class RealizationGridLayer const data = getHelperDependency(realizationGridDataDep); if (!gridName || !data) { - return [0, 0]; + return CancelUpdate; } const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; @@ -295,7 +296,7 @@ export class RealizationGridLayer const data = getHelperDependency(realizationGridDataDep); if (!gridName || !gridAttribute || !data) { - return []; + return CancelUpdate; } const gridAttributeArr = @@ -306,8 +307,8 @@ export class RealizationGridLayer new Set( gridAttributeArr .filter((attr) => attr.property_name === gridAttribute) - .map((gridAttribute) => gridAttribute.iso_date_or_interval ?? "NO_TIME") - ) + .map((gridAttribute) => gridAttribute.iso_date_or_interval ?? "NO_TIME"), + ), ), ]; diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer.ts index ddb4d9b58..389e11d86 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer.ts @@ -1,4 +1,4 @@ -import type { SurfaceDataPng_api } from "@api"; +import type { SurfaceAttributeType_api, SurfaceDataPng_api } from "@api"; import { SurfaceTimeType_api, getRealizationSurfacesMetadataOptions, getSurfaceDataOptions } from "@api"; import type { CustomDataLayerImplementation, @@ -42,8 +42,10 @@ export class RealizationSurfaceLayer settings = realizationSurfaceSettings; private _dataFormat: SurfaceDataFormat; + private _attributeTypesFilter: SurfaceAttributeType_api[] = []; - constructor(dataFormat?: SurfaceDataFormat) { + constructor(attributeTypesFilter?: SurfaceAttributeType_api[], dataFormat?: SurfaceDataFormat) { + this._attributeTypesFilter = attributeTypesFilter ?? []; this._dataFormat = dataFormat ?? SurfaceDataFormat.PNG; } @@ -145,7 +147,17 @@ export class RealizationSurfaceLayer } const availableAttributes = [ - ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), + ...Array.from( + new Set( + data.surfaces + .filter( + (el) => + this._attributeTypesFilter.includes(el.attribute_type) || + this._attributeTypesFilter.length === 0, + ) + .map((surface) => surface.attribute_name), + ), + ), ]; return availableAttributes; diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer.ts index d69a1b56f..3812e0071 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer.ts @@ -1,4 +1,4 @@ -import type { SurfaceDataPng_api } from "@api"; +import type { SurfaceAttributeType_api, SurfaceDataPng_api } from "@api"; import { SurfaceStatisticFunction_api, SurfaceTimeType_api, @@ -49,8 +49,10 @@ export class StatisticalSurfaceLayer settings = statisicalSurfaceSettings; private _dataFormat: SurfaceDataFormat; + private _attributeTypesFilter: SurfaceAttributeType_api[] = []; - constructor(dataFormat?: SurfaceDataFormat) { + constructor(attributeTypesFilter?: SurfaceAttributeType_api[], dataFormat?: SurfaceDataFormat) { + this._attributeTypesFilter = attributeTypesFilter ?? []; this._dataFormat = dataFormat ?? SurfaceDataFormat.PNG; } @@ -116,7 +118,7 @@ export class StatisticalSurfaceLayer sensitivityName: sensitivity.name, sensitivityCase: sensitivityCase.name, }); - }) + }), ); return availableSensitivityPairs; }); @@ -147,7 +149,17 @@ export class StatisticalSurfaceLayer } const availableAttributes = [ - ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), + ...Array.from( + new Set( + data.surfaces + .filter( + (el) => + this._attributeTypesFilter.includes(el.attribute_type) || + this._attributeTypesFilter.length === 0, + ) + .map((surface) => surface.attribute_name), + ), + ), ]; return availableAttributes; @@ -163,8 +175,8 @@ export class StatisticalSurfaceLayer const availableSurfaceNames = [ ...Array.from( new Set( - data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name) - ) + data.surfaces.filter((surface) => surface.attribute_name === attribute).map((el) => el.name), + ), ), ]; @@ -186,8 +198,8 @@ export class StatisticalSurfaceLayer new Set( data.surfaces .filter((surface) => surface.attribute_name === attribute && surface.name === surfaceName) - .map((el) => el.time_type) - ) + .map((el) => el.time_type), + ), ), ]; @@ -244,7 +256,7 @@ export class StatisticalSurfaceLayer const sensitivityRealizations = sensitivity?.realizations ?? []; filteredRealizations = filteredRealizations.filter((realization) => - sensitivityRealizations.includes(realization) + sensitivityRealizations.includes(realization), ); } diff --git a/frontend/src/modules/_shared/LayerFramework/layers/layerTypes.ts b/frontend/src/modules/_shared/LayerFramework/layers/layerTypes.ts index 170ea7e25..4a3d2a073 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/layerTypes.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/layerTypes.ts @@ -2,12 +2,14 @@ export enum LayerType { DRILLED_WELLBORE_PICKS = "DRILLED_WELLBORE_PICKS", DRILLED_WELL_TRAJECTORIES = "DRILLED_WELL_TRAJECTORIES", REALIZATION_GRID = "REALIZATION_GRID", - REALIZATION_SURFACE = "REALIZATION_SURFACE", + REALIZATION_SURFACE_2D = "REALIZATION_SURFACE_2D", + REALIZATION_SURFACE_3D = "REALIZATION_SURFACE_3D", REALIZATION_POLYGONS = "REALIZATION_POLYGONS", REALIZATION_SEISMIC_DEPTH_SLICE = "REALIZATION_SEISMIC_DEPTH_SLICE", REALIZATION_SEISMIC_INLINE = "REALIZATION_SEISMIC_INLINE", REALIZATION_SEISMIC_CROSSLINE = "REALIZATION_SEISMIC_CROSSLINE", - OBSERVED_SURFACE = "OBSERVED_SURFACE", + OBSERVED_SURFACE_2D = "OBSERVED_SURFACE_2D", INTERSECTION_REALIZATION_GRID = "INTERSECTION_REALIZATION_GRID", - STATISTICAL_SURFACE = "STATISTICAL_SURFACE", + STATISTICAL_SURFACE_2D = "STATISTICAL_SURFACE_2D", + STATISTICAL_SURFACE_3D = "STATISTICAL_SURFACE_3D", } diff --git a/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts b/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts index ddfedeac0..2e292998d 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts @@ -1,3 +1,4 @@ +import { SurfaceAttributeType_api } from "@api"; import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer"; import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; @@ -16,11 +17,17 @@ import { LayerType } from "./layerTypes"; LayerRegistry.registerLayer(LayerType.DRILLED_WELLBORE_PICKS, DrilledWellborePicksLayer); LayerRegistry.registerLayer(LayerType.DRILLED_WELL_TRAJECTORIES, DrilledWellTrajectoriesLayer); LayerRegistry.registerLayer(LayerType.INTERSECTION_REALIZATION_GRID, IntersectionRealizationGridLayer); -LayerRegistry.registerLayer(LayerType.REALIZATION_SURFACE, RealizationSurfaceLayer); +LayerRegistry.registerLayer(LayerType.REALIZATION_SURFACE_2D, RealizationSurfaceLayer); LayerRegistry.registerLayer(LayerType.REALIZATION_POLYGONS, RealizationPolygonsLayer); LayerRegistry.registerLayer(LayerType.REALIZATION_SEISMIC_DEPTH_SLICE, RealizationSeismicDepthSliceLayer); LayerRegistry.registerLayer(LayerType.REALIZATION_SEISMIC_INLINE, RealizationSeismicInlineLayer); LayerRegistry.registerLayer(LayerType.REALIZATION_SEISMIC_CROSSLINE, RealizationSeismicCrosslineLayer); -LayerRegistry.registerLayer(LayerType.OBSERVED_SURFACE, ObservedSurfaceLayer); -LayerRegistry.registerLayer(LayerType.STATISTICAL_SURFACE, StatisticalSurfaceLayer); +LayerRegistry.registerLayer(LayerType.OBSERVED_SURFACE_2D, ObservedSurfaceLayer); +LayerRegistry.registerLayer(LayerType.STATISTICAL_SURFACE_2D, StatisticalSurfaceLayer); LayerRegistry.registerLayer(LayerType.REALIZATION_GRID, RealizationGridLayer); +LayerRegistry.registerLayer(LayerType.REALIZATION_SURFACE_3D, RealizationSurfaceLayer, [ + [SurfaceAttributeType_api.DEPTH], +]); +LayerRegistry.registerLayer(LayerType.STATISTICAL_SURFACE_3D, StatisticalSurfaceLayer, [ + [SurfaceAttributeType_api.DEPTH], +]); diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerRangeSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerRangeSetting.tsx index 8b282838f..87b5898c3 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerRangeSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/GridLayerRangeSetting.tsx @@ -6,7 +6,6 @@ import type { CustomSettingImplementation, SettingComponentProps, } from "../../interfacesAndTypes/customSettingImplementation"; -import type { MakeAvailableValuesTypeBasedOnCategory } from "../../interfacesAndTypes/utils"; import type { SettingCategory } from "../settingsDefinitions"; type ValueType = [number, number] | null; @@ -37,50 +36,6 @@ export class GridLayerRangeSetting implements CustomSettingImplementation - ): boolean { - if (value === null) { - return false; - } - - if (!availableValues) { - return false; - } - - const min = availableValues[0]; - const max = availableValues[1]; - - if (max === null || min === null) { - return false; - } - - return value[0] >= min && value[0] <= max; - } - - fixupValue( - currentValue: ValueType, - availableValues: MakeAvailableValuesTypeBasedOnCategory - ): ValueType { - if (!availableValues) { - return null; - } - - const min = availableValues[0]; - const max = availableValues[1]; - - if (max === null) { - return null; - } - - if (currentValue === null) { - return [min, max]; - } - - return [Math.max(currentValue[0], min), Math.min(currentValue[1], max)]; - } - makeComponent(): (props: SettingComponentProps) => React.ReactNode { return function RangeSlider(props: SettingComponentProps) { function handleChange(_: any, value: number | number[]) { diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicSliceSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicSliceSetting.tsx index c9775ce28..6c430e0b5 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicSliceSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SeismicSliceSetting.tsx @@ -24,31 +24,9 @@ export class SeismicSliceSetting implements CustomSettingImplementation - ): boolean { - if (value === null) { - return false; - } - - if (availableValues.length < 2) { - return false; - } - - const min = 0; - const max = availableValues[1]; - - if (max === null) { - return false; - } - - return value >= min && value <= max; - } - fixupValue( currentValue: ValueType, - availableValues: MakeAvailableValuesTypeBasedOnCategory + availableValues: MakeAvailableValuesTypeBasedOnCategory, ): ValueType { if (availableValues.length < 2) { return null; @@ -91,10 +69,10 @@ export class SeismicSliceSetting implements CustomSettingImplementation min + i * step + (_, i) => min + i * step, ); value = allowedValues.reduce((prev, curr) => - Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev + Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev, ); } diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/utils/colors.ts b/frontend/src/modules/_shared/LayerFramework/visualization/utils/colors.ts index b25fbefb8..dc32b1f28 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/utils/colors.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/utils/colors.ts @@ -13,9 +13,12 @@ export function makeColorMapFunctionFromColorScale( return undefined; } + const localColorScale = colorScale.clone(); + localColorScale.setRange(valueMin, valueMax); + return (value: number) => { const nonNormalizedValue = unnormalize ? value * (valueMax - valueMin) + valueMin : value; - const interpolatedColor = colorScale.getColorForValue(nonNormalizedValue); + const interpolatedColor = localColorScale.getColorForValue(nonNormalizedValue); const color = parse(interpolatedColor) as Rgb; if (color === undefined) { return [0, 0, 0]; @@ -23,3 +26,4 @@ export function makeColorMapFunctionFromColorScale( return [color.r * 255, color.g * 255, color.b * 255]; }; } +0; diff --git a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts index d04144d56..7ec67e274 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer.ts @@ -73,39 +73,53 @@ export class SeismicFenceMeshLayer extends CompositeLayer>>) { - const updateRequired = + const meshRecomputationRequired = !isEqual(oldProps.data?.sections.length, props.data?.sections.length) || - !isEqual(oldProps.data?.sections, props.data?.sections); + !isEqual(oldProps.data?.sections, props.data?.sections) || + !isEqual(oldProps.zIncreaseDownwards, props.zIncreaseDownwards); - if (updateRequired) { - this.setState({ - ...this.state, - isLoaded: false, - }); + const colorMapFunctionChanged = !isEqual(oldProps.colorMapFunction, props.colorMapFunction); - if (props.isLoading) { - return; - } + if (!meshRecomputationRequired && !colorMapFunctionChanged) { + return; + } + + if (props.isLoading) { + return; + } + if (meshRecomputationRequired) { this.rebuildMesh(); } + + if (colorMapFunctionChanged) { + this.recolorMesh(); + } } private calcNumVerticesForSection(section: SeismicFenceSection): number { @@ -155,28 +169,26 @@ export class SeismicFenceMeshLayer extends CompositeLayer { - const verticesArr = new Float32Array(this._sharedVerticesBuffer!); - const indicesArr = new Uint32Array(this._sharedIndicesBuffer!); - this.setState({ - geometry: new Geometry({ - attributes: { - positions: verticesArr, - colors: { - value: this._colorsArray, - size: 4, - }, - }, - topology: "triangle-list", - indices: indicesArr, - }), - isLoaded: true, - }); + const verticesArr = new Float32Array(this._sharedVerticesBuffer!); + const indicesArr = new Uint32Array(this._sharedIndicesBuffer!); - this.props.reportBoundingBox?.({ - layerBoundingBox: this.calcBoundingBox(), - }); + this.setState({ + ...this.state, + geometry: new Geometry({ + attributes: { + ...geometry.attributes, + positions: verticesArr, + }, + topology: "triangle-list", + indices: indicesArr, + }), + meshCreated: true, + }); + + this.props.reportBoundingBox?.({ + layerBoundingBox: this.calcBoundingBox(), }); } } @@ -200,6 +212,8 @@ export class SeismicFenceMeshLayer extends CompositeLayer { + this.setState({ + ...this.state, + geometry: new Geometry({ + attributes: { + ...geometry.attributes, + colors: { + value: this._colorsArray, + size: 4, + }, + }, + topology: "triangle-list", + indices: geometry.indices, + }), + colorsArrayCreated: true, + }); + }); + } + + private async makeColorsArray() { const { data, colorMapFunction } = this.props; this._colorsArray = new Float32Array( - data.sections.reduce((acc, section) => acc + section.properties.length * 4, 0) + data.sections.reduce((acc, section) => acc + section.properties.length * 4, 0), ); let colorIndex = 0; @@ -310,19 +348,19 @@ export class SeismicFenceMeshLayer extends CompositeLayer[] = []; - if ((isLoading || !isLoaded) && loadingGeometry) { + if ((isLoading || !meshCreated || !colorsArrayCreated) && loadingGeometry) { layers.push( new PreviewLayer({ id: "seismic-fence-mesh-layer-loading", @@ -330,7 +368,7 @@ export class SeismicFenceMeshLayer extends CompositeLayer Date: Thu, 27 Mar 2025 16:13:44 +0100 Subject: [PATCH 68/97] wip --- .../DrilledWellTrajectoriesLayer.ts | 6 ++++- .../DrilledWellborePicksLayer.ts | 8 ++++++- .../DrilledWellboresSetting.tsx | 8 +++---- .../settings/settingsDefinitions.ts | 22 +++++++++---------- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer.ts index 5f5aae9f8..86bc774dd 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer.ts @@ -104,6 +104,7 @@ export class DrilledWellTrajectoriesLayer }), }); }); + availableSettingsUpdater(Setting.SMDA_WELLBORE_HEADERS, ({ getHelperDependency }) => { const wellboreHeaders = getHelperDependency(wellboreHeadersDep); @@ -111,7 +112,10 @@ export class DrilledWellTrajectoriesLayer return []; } - return wellboreHeaders; + return wellboreHeaders.map((header) => ({ + wellboreUuid: header.wellboreUuid, + uniqueWellboreIdentifier: header.uniqueWellboreIdentifier, + })); }); } } diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer.ts index f5cf510e4..a0f6bf765 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer.ts @@ -84,6 +84,7 @@ export class DrilledWellborePicksLayer defineDependencies({ helperDependency, availableSettingsUpdater, + storedDataUpdater, workbenchSession, queryClient, }: DefineDependenciesArgs) { @@ -153,7 +154,12 @@ export class DrilledWellborePicksLayer return []; } - return wellboreHeaders; + return wellboreHeaders.map((header) => { + return { + wellboreUuid: header.wellboreUuid, + uniqueWellboreIdentifier: header.uniqueWellboreIdentifier, + }; + }); }); availableSettingsUpdater(Setting.SURFACE_NAME, ({ getHelperDependency }) => { diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/DrilledWellboresSetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/DrilledWellboresSetting.tsx index 89257b8cf..6415e494e 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/DrilledWellboresSetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/DrilledWellboresSetting.tsx @@ -13,7 +13,7 @@ import type { import type { MakeAvailableValuesTypeBasedOnCategory } from "../../interfacesAndTypes/utils"; import type { SettingCategory } from "../settingsDefinitions"; -type ValueType = WellboreHeader_api[] | null; +type ValueType = Pick[] | null; export class DrilledWellboresSetting implements CustomSettingImplementation { defaultValue: ValueType = null; @@ -24,14 +24,14 @@ export class DrilledWellboresSetting implements CustomSettingImplementation + availableValues: MakeAvailableValuesTypeBasedOnCategory, ): ValueType { if (!currentValue) { return availableValues; } const matchingValues = currentValue.filter((value) => - availableValues.some((availableValue) => availableValue.wellboreUuid === value.wellboreUuid) + availableValues.some((availableValue) => availableValue.wellboreUuid === value.wellboreUuid), ); if (matchingValues.length === 0) { return availableValues; @@ -64,7 +64,7 @@ export class DrilledWellboresSetting implements CustomSettingImplementation props.value?.map((ident) => ident.wellboreUuid) ?? [], - [props.value] + [props.value], ); return ( diff --git a/frontend/src/modules/_shared/LayerFramework/settings/settingsDefinitions.ts b/frontend/src/modules/_shared/LayerFramework/settings/settingsDefinitions.ts index 825a82d7a..f9f1a03fa 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/settingsDefinitions.ts +++ b/frontend/src/modules/_shared/LayerFramework/settings/settingsDefinitions.ts @@ -91,7 +91,7 @@ export type SettingTypes = { [Setting.SEISMIC_INLINE]: number | null; [Setting.SENSITIVITY]: SensitivityNameCasePair | null; [Setting.SHOW_GRID_LINES]: boolean; - [Setting.SMDA_WELLBORE_HEADERS]: WellboreHeader_api[] | null; + [Setting.SMDA_WELLBORE_HEADERS]: Pick[] | null; [Setting.STATISTIC_FUNCTION]: SurfaceStatisticFunction_api; [Setting.SURFACE_NAME]: string | null; [Setting.TIME_OR_INTERVAL]: string | null; @@ -103,7 +103,7 @@ export type PossibleSettingsForCategory = { interface FixupCategoryValue< TCategory extends SettingCategory, - TValue = SettingTypes[PossibleSettingsForCategory] + TValue = SettingTypes[PossibleSettingsForCategory], > { (value: TValue, availableValues: AvailableValuesType>): TValue; } @@ -114,7 +114,7 @@ type SettingCategoryFixupMap = { interface CheckIfCategoryValueIsValid< TCategory extends SettingCategory, - TValue = SettingTypes[PossibleSettingsForCategory] + TValue = SettingTypes[PossibleSettingsForCategory], > { (value: TValue, availableValues: AvailableValuesType>): boolean; } @@ -127,7 +127,7 @@ interface AvailableValuesIntersectionReducer ( accumulator: AvailableValuesType>, currentAvailableValues: AvailableValuesType>, - currentIndex: number + currentIndex: number, ): AvailableValuesType>; } @@ -308,13 +308,11 @@ type UnionForAny = T extends never ? "A" : "B"; // Returns true if type is any, or false for any other type. type IsStrictlyAny = UnionToIntersection> extends never ? true : false; -export type MakeSettingTypesMap< - T extends readonly (keyof SettingTypes)[], - AllowNull extends boolean = false -> = IsStrictlyAny extends true - ? any - : { - [K in T[number]]: AllowNull extends false ? SettingTypes[K] : SettingTypes[K] | null; - }; +export type MakeSettingTypesMap = + IsStrictlyAny extends true + ? any + : { + [K in T[number]]: AllowNull extends false ? SettingTypes[K] : SettingTypes[K] | null; + }; export type Settings = ReadonlyArray & { __brand?: "MyType" }; From 29c4821cbea3444e95e7df3acbee52deb6ebd181 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 27 Mar 2025 16:13:55 +0100 Subject: [PATCH 69/97] wip --- .../makeDrilledWellTrajectoriesLayer.ts | 122 ++++++++++++++++++ .../view/components/LayersWrapper.tsx | 7 + 2 files changed, 129 insertions(+) create mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts new file mode 100644 index 000000000..755dd7fd9 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts @@ -0,0 +1,122 @@ +import type { WellboreTrajectory_api } from "@api"; +import * as bbox from "@lib/utils/bbox"; +import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellTrajectoriesBoundingBox"; +import { WellsLayer } from "@modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer"; +import type { WellsLayerData } from "@modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer"; + +import type { Feature, GeoJsonProperties, GeometryCollection, LineString, Point } from "geojson"; + +function wellTrajectoryToGeojson( + wellTrajectory: WellboreTrajectory_api, +): Feature { + const point: Point = { + type: "Point", + coordinates: [wellTrajectory.eastingArr[0], wellTrajectory.northingArr[0], -wellTrajectory.tvdMslArr[0]], + }; + + const coordinates: LineString = { + type: "LineString", + coordinates: zipCoords(wellTrajectory.eastingArr, wellTrajectory.northingArr, wellTrajectory.tvdMslArr), + }; + + const color = [100, 100, 100]; + const lineWidth = 2; + const wellHeadSize = 1; + + const geometryCollection: Feature = { + type: "Feature", + geometry: { + type: "GeometryCollection", + geometries: [point, coordinates], + }, + properties: { + uuid: wellTrajectory.wellboreUuid, + name: wellTrajectory.uniqueWellboreIdentifier, + uwi: wellTrajectory.uniqueWellboreIdentifier, + color, + md: [wellTrajectory.mdArr], + lineWidth, + wellHeadSize, + }, + }; + + return geometryCollection; +} + +function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { + const coords: number[][] = []; + for (let i = 0; i < xArr.length; i++) { + coords.push([xArr[i], yArr[i], -zArr[i]]); + } + + return coords; +} + +export function makeDrilledWellTrajectoriesLayer( + args: FactoryFunctionArgs, +): WellsLayer | null { + const { id, getData } = args; + + const fieldWellboreTrajectoriesData = getData(); + + if (!fieldWellboreTrajectoriesData) { + return null; + } + + // Filter out some wellbores that are known to be not working - this is a temporary solution + const tempWorkingWellsData = fieldWellboreTrajectoriesData.filter( + (el) => el.uniqueWellboreIdentifier !== "NO 34/4-K-3 AH", + ); + + const wellLayerDataFeatures = tempWorkingWellsData.map((well) => wellTrajectoryToGeojson(well)); + + function getLineStyleWidth(object: Feature): number { + if (object.properties && "lineWidth" in object.properties) { + return object.properties.lineWidth as number; + } + return 2; + } + + function getWellHeadStyleWidth(object: Feature): number { + if (object.properties && "wellHeadSize" in object.properties) { + return object.properties.wellHeadSize as number; + } + return 1; + } + + function getColor(object: Feature): [number, number, number, number] { + if (object.properties && "color" in object.properties) { + return object.properties.color as [number, number, number, number]; + } + return [50, 50, 50, 100]; + } + + const wellsLayerData: WellsLayerData = []; + for (const wellboreData of fieldWellboreTrajectoriesData) { + const properties = { + uuid: wellboreData.wellboreUuid, + name: wellboreData.uniqueWellboreIdentifier, + mdArray: wellboreData.mdArr, + }; + const coordinates: [number, number, number][] = wellboreData.eastingArr.map((easting, index) => { + return [easting, wellboreData.northingArr[index], -wellboreData.tvdMslArr[index]]; + }); + wellsLayerData.push({ properties, coordinates }); + } + + const boundingBox = makeDrilledWellTrajectoriesBoundingBox(args); + + if (!boundingBox) { + return null; + } + + const wellsLayer = new WellsLayer({ + id: id, + data: wellsLayerData, + zIncreaseDownwards: true, + boundingBox: bbox.toNumArray(boundingBox), + }); + + return wellsLayer; +} diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index baddb783d..b4892f1fc 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -13,6 +13,10 @@ import { PreferredViewLayout } from "@modules/2DViewer/types"; import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer"; import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; +<<<<<<< Updated upstream +======= +import { makeDrilledWellTrajectoriesLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer"; +>>>>>>> Stashed changes import { makeRealizationSurfaceLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer"; import { Plane, @@ -41,7 +45,10 @@ import { makeDrilledWellborePicksBoundingBox } from "@modules/_shared/LayerFrame import { makePolygonDataBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makePolygonDataBoundingBox"; import { makeRealizationGridBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationGridBoundingBox"; import { makeSurfaceLayerBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeSurfaceLayerBoundingBox"; +<<<<<<< Updated upstream import { makeDrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer"; +======= +>>>>>>> Stashed changes import { makeDrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellborePicksLayer"; import { makeObservedSurfaceLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeObservedSurfaceLayer"; import { makeRealizationGridLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer"; From f6364a4f4a30169d7fa8aa526357e48c31a37f68 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 27 Mar 2025 17:16:35 +0100 Subject: [PATCH 70/97] wip --- .../makeDrilledWellTrajectoriesLayer.ts | 76 ------------------- .../layerManagerComponentWrapper.tsx | 34 ++++++--- .../view/components/LayersWrapper.tsx | 38 +++------- .../layers/registerAllLayers.ts | 4 +- .../WellsLayer/WellsLayer.ts | 7 +- .../WellsLayer/_private/PipeLayer.ts | 12 +-- 6 files changed, 49 insertions(+), 122 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts index 755dd7fd9..dc1f769fe 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts @@ -5,54 +5,6 @@ import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/LayerFr import { WellsLayer } from "@modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer"; import type { WellsLayerData } from "@modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer"; -import type { Feature, GeoJsonProperties, GeometryCollection, LineString, Point } from "geojson"; - -function wellTrajectoryToGeojson( - wellTrajectory: WellboreTrajectory_api, -): Feature { - const point: Point = { - type: "Point", - coordinates: [wellTrajectory.eastingArr[0], wellTrajectory.northingArr[0], -wellTrajectory.tvdMslArr[0]], - }; - - const coordinates: LineString = { - type: "LineString", - coordinates: zipCoords(wellTrajectory.eastingArr, wellTrajectory.northingArr, wellTrajectory.tvdMslArr), - }; - - const color = [100, 100, 100]; - const lineWidth = 2; - const wellHeadSize = 1; - - const geometryCollection: Feature = { - type: "Feature", - geometry: { - type: "GeometryCollection", - geometries: [point, coordinates], - }, - properties: { - uuid: wellTrajectory.wellboreUuid, - name: wellTrajectory.uniqueWellboreIdentifier, - uwi: wellTrajectory.uniqueWellboreIdentifier, - color, - md: [wellTrajectory.mdArr], - lineWidth, - wellHeadSize, - }, - }; - - return geometryCollection; -} - -function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { - const coords: number[][] = []; - for (let i = 0; i < xArr.length; i++) { - coords.push([xArr[i], yArr[i], -zArr[i]]); - } - - return coords; -} - export function makeDrilledWellTrajectoriesLayer( args: FactoryFunctionArgs, ): WellsLayer | null { @@ -64,34 +16,6 @@ export function makeDrilledWellTrajectoriesLayer( return null; } - // Filter out some wellbores that are known to be not working - this is a temporary solution - const tempWorkingWellsData = fieldWellboreTrajectoriesData.filter( - (el) => el.uniqueWellboreIdentifier !== "NO 34/4-K-3 AH", - ); - - const wellLayerDataFeatures = tempWorkingWellsData.map((well) => wellTrajectoryToGeojson(well)); - - function getLineStyleWidth(object: Feature): number { - if (object.properties && "lineWidth" in object.properties) { - return object.properties.lineWidth as number; - } - return 2; - } - - function getWellHeadStyleWidth(object: Feature): number { - if (object.properties && "wellHeadSize" in object.properties) { - return object.properties.wellHeadSize as number; - } - return 1; - } - - function getColor(object: Feature): [number, number, number, number] { - if (object.properties && "color" in object.properties) { - return object.properties.color as [number, number, number, number]; - } - return [50, 50, 50, 100]; - } - const wellsLayerData: WellsLayerData = []; for (const wellboreData of fieldWellboreTrajectoriesData) { const properties = { diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index 3bb816bef..f312a61f2 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -1,16 +1,16 @@ -import React from "react"; +import type React from "react"; import { Icon } from "@equinor/eds-core-react"; -import { color_palette, grid_layer, settings, surface_layer, wellbore } from "@equinor/eds-icons"; -import { WorkbenchSession } from "@framework/WorkbenchSession"; -import { WorkbenchSettings } from "@framework/WorkbenchSettings"; +import { color_palette, fault, grid_layer, settings, surface_layer, wellbore } from "@equinor/eds-icons"; +import type { WorkbenchSession } from "@framework/WorkbenchSession"; +import type { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { Menu } from "@lib/components/Menu"; import { MenuButton } from "@lib/components/MenuButton"; import { MenuHeading } from "@lib/components/MenuHeading"; import { MenuItem } from "@lib/components/MenuItem"; import { PreferredViewLayout } from "@modules/2DViewer/types"; -import { LayersActionGroup } from "@modules/_shared/LayerFramework/LayersActions"; -import { GroupDelegate, GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; +import type { LayersActionGroup } from "@modules/_shared/LayerFramework/LayersActions"; +import { type GroupDelegate, GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; import { DataLayer } from "@modules/_shared/LayerFramework/framework/DataLayer/DataLayer"; import type { DataLayerManager } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; import { LayerManagerComponent } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManagerComponent"; @@ -72,17 +72,14 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper case "color-scale": groupDelegate.appendChild(new SharedSetting(Setting.COLOR_SCALE, null, props.layerManager)); return; - case "observed-surface": - groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.OBSERVED_SURFACE_2D, props.layerManager)); - return; case "statistical-surface": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.STATISTICAL_SURFACE_2D, props.layerManager), + LayerRegistry.makeLayer(LayerType.STATISTICAL_SURFACE_3D, props.layerManager), ); return; case "realization-surface": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.REALIZATION_SURFACE_2D, props.layerManager), + LayerRegistry.makeLayer(LayerType.REALIZATION_SURFACE_3D, props.layerManager), ); return; case "realization-polygons": @@ -287,6 +284,11 @@ const LAYER_ACTIONS: LayersActionGroup[] = [ { label: "Surface", children: [ + { + identifier: "statistical-surface", + icon: , + label: "Statistical Surface", + }, { identifier: "realization-surface", icon: , @@ -294,6 +296,16 @@ const LAYER_ACTIONS: LayersActionGroup[] = [ }, ], }, + { + label: "Polygons", + children: [ + { + identifier: "realization-polygons", + icon: , + label: "Realization Polygons", + }, + ], + }, { label: "Seismic", children: [ diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index b4892f1fc..c238063d1 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -1,22 +1,19 @@ import React from "react"; import { View as DeckGlView } from "@deck.gl/core"; -import { ViewContext } from "@framework/ModuleContext"; +import type { ViewContext } from "@framework/ModuleContext"; import { useViewStatusWriter } from "@framework/StatusWriter"; -import { WorkbenchSession } from "@framework/WorkbenchSession"; -import { WorkbenchSettings } from "@framework/WorkbenchSettings"; +import type { WorkbenchSession } from "@framework/WorkbenchSession"; +import type { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { useElementSize } from "@lib/hooks/useElementSize"; import * as bbox from "@lib/utils/boundingBox"; import { makeColorScaleAnnotation } from "@modules/2DViewer/LayerFramework/annotations/makeColorScaleAnnotation"; -import { Interfaces } from "@modules/2DViewer/interfaces"; +import type { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer"; import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; -<<<<<<< Updated upstream -======= import { makeDrilledWellTrajectoriesLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer"; ->>>>>>> Stashed changes import { makeRealizationSurfaceLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer"; import { Plane, @@ -28,7 +25,6 @@ import { } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; -import { ObservedSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/ObservedSurfaceLayer"; import { RealizationGridLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer"; import { RealizationPolygonsLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationPolygonsLayer"; import { RealizationSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer"; @@ -36,28 +32,22 @@ import { StatisticalSurfaceLayer } from "@modules/_shared/LayerFramework/layers/ import { LayerType } from "@modules/_shared/LayerFramework/layers/layerTypes"; import { type Annotation, - LayerWithPosition, + type LayerWithPosition, VisualizationFactory, - VisualizationTarget, + type VisualizationTarget, } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellTrajectoriesBoundingBox"; import { makeDrilledWellborePicksBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellborePicksBoundingBox"; import { makePolygonDataBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makePolygonDataBoundingBox"; import { makeRealizationGridBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationGridBoundingBox"; import { makeSurfaceLayerBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeSurfaceLayerBoundingBox"; -<<<<<<< Updated upstream -import { makeDrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer"; -======= ->>>>>>> Stashed changes import { makeDrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellborePicksLayer"; -import { makeObservedSurfaceLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeObservedSurfaceLayer"; import { makeRealizationGridLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer"; import { makeRealizationPolygonsLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationPolygonsLayer"; -import { makeStatisticalSurfaceLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeStatisticalSurfaceLayer"; import { ColorLegendsContainer } from "@modules/_shared/components/ColorLegendsContainer"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; -import { BoundingBox3D, ViewportType } from "@webviz/subsurface-viewer"; -import { ViewsType } from "@webviz/subsurface-viewer/dist/SubsurfaceViewer"; +import type { BoundingBox3D, ViewportType } from "@webviz/subsurface-viewer"; +import type { ViewsType } from "@webviz/subsurface-viewer/dist/SubsurfaceViewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; import { InteractionWrapper } from "./InteractionWrapper"; @@ -66,25 +56,19 @@ import { PlaceholderLayer } from "../../../_shared/customDeckGlLayers/Placeholde const VISUALIZATION_FACTORY = new VisualizationFactory(); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.OBSERVED_SURFACE_2D, ObservedSurfaceLayer, { - makeVisualizationFunction: makeObservedSurfaceLayer, - calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, - makeAnnotationsFunction: makeColorScaleAnnotation, -}); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_SURFACE_2D, RealizationSurfaceLayer, { +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_SURFACE_3D, RealizationSurfaceLayer, { makeVisualizationFunction: makeRealizationSurfaceLayer, calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, }); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.STATISTICAL_SURFACE_2D, StatisticalSurfaceLayer, { - makeVisualizationFunction: makeStatisticalSurfaceLayer, +VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.STATISTICAL_SURFACE_3D, StatisticalSurfaceLayer, { + makeVisualizationFunction: makeRealizationSurfaceLayer, calculateBoundingBoxFunction: makeSurfaceLayerBoundingBox, makeAnnotationsFunction: makeColorScaleAnnotation, }); VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_POLYGONS, RealizationPolygonsLayer, { makeVisualizationFunction: makeRealizationPolygonsLayer, calculateBoundingBoxFunction: makePolygonDataBoundingBox, - makeAnnotationsFunction: makeColorScaleAnnotation, }); VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_GRID, RealizationGridLayer, { makeVisualizationFunction: makeRealizationGridLayer, diff --git a/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts b/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts index 2e292998d..c013a92d7 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/registerAllLayers.ts @@ -10,7 +10,7 @@ import { IntersectionRealizationGridLayer } from "./implementations/Intersection import { ObservedSurfaceLayer } from "./implementations/ObservedSurfaceLayer"; import { RealizationGridLayer } from "./implementations/RealizationGridLayer"; import { RealizationPolygonsLayer } from "./implementations/RealizationPolygonsLayer"; -import { RealizationSurfaceLayer } from "./implementations/RealizationSurfaceLayer"; +import { RealizationSurfaceLayer, SurfaceDataFormat } from "./implementations/RealizationSurfaceLayer"; import { StatisticalSurfaceLayer } from "./implementations/StatisticalSurfaceLayer"; import { LayerType } from "./layerTypes"; @@ -27,7 +27,9 @@ LayerRegistry.registerLayer(LayerType.STATISTICAL_SURFACE_2D, StatisticalSurface LayerRegistry.registerLayer(LayerType.REALIZATION_GRID, RealizationGridLayer); LayerRegistry.registerLayer(LayerType.REALIZATION_SURFACE_3D, RealizationSurfaceLayer, [ [SurfaceAttributeType_api.DEPTH], + SurfaceDataFormat.FLOAT, ]); LayerRegistry.registerLayer(LayerType.STATISTICAL_SURFACE_3D, StatisticalSurfaceLayer, [ [SurfaceAttributeType_api.DEPTH], + SurfaceDataFormat.FLOAT, ]); diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts index cedccd137..483775327 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts @@ -110,7 +110,12 @@ export class WellsLayer extends CompositeLayer { }), }; }), - material: true, + material: { + ambient: 0.2, + diffuse: 0.6, + shininess: 132, + specularColor: [255, 255, 255], + }, pickable: true, // @ts-expect-error - This is how deck.gl expects the state to be defined parameters: { depthTest: true }, diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts index db45bcab3..1824f7350 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts @@ -12,7 +12,7 @@ import { import { Vec3 } from "@lib/utils/vec3"; import * as vec3 from "@lib/utils/vec3"; import { Geometry, Model } from "@luma.gl/engine"; -import { phongLighting } from "@luma.gl/shadertools"; +import { phongLighting, phongMaterial } from "@luma.gl/shadertools"; import { isEqual } from "lodash"; @@ -110,7 +110,7 @@ export class PipeLayer extends Layer { const model = new Model(context.device, { id: `${this.id}-mesh-${idx}`, geometry: mesh, - modules: [project32, phongLighting, picking, pipeUniforms], + modules: [project32, phongLighting, picking, pipeUniforms, phongMaterial], vs: vertexShader, fs: fragmentShader, }); @@ -159,7 +159,7 @@ export class PipeLayer extends Layer { const vertices = new Float32Array((numContours + 2) * numVerticesPerContour * 3 + 2 * 3); const indices = new Uint32Array( - (numContours - 1) * numVerticesPerContour * 6 + numVerticesPerContour * 2 * 3 + (numContours - 1) * numVerticesPerContour * 6 + numVerticesPerContour * 2 * 3, ); const normals = new Float32Array((numContours + 2) * numVerticesPerContour * 3 + 2 * 3); @@ -229,8 +229,8 @@ export class PipeLayer extends Layer { const endNormal = vec3.normalize( vec3.cross( pipe.getNormals()[pipe.getNormals().length - 1][0], - pipe.getNormals()[pipe.getNormals().length - 1][1] - ) + pipe.getNormals()[pipe.getNormals().length - 1][1], + ), ); for (let j = 0; j < numVerticesPerContour; j++) { const vertex = pipe.getContours()[numContours - 1][j]; @@ -271,7 +271,7 @@ export class PipeLayer extends Layer { }, indices: indices, id: data[idx].id, - }) + }), ); } From 76bae9754a4f7070cc91923ac21b40ea55aeafeb Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 28 Mar 2025 16:40:04 +0100 Subject: [PATCH 71/97] wip --- .../services/sumo_access/grid3d_access.py | 4 +- frontend/src/lib/utils/geometry.ts | 12 +- frontend/src/lib/utils/vec3.ts | 12 +- .../layerManagerComponentWrapper.tsx | 8 +- .../view/components/LayersWrapper.tsx | 6 +- .../makeSeismicFenceMeshLayer.ts | 9 +- .../layerManagerComponentWrapper.tsx | 13 +- .../view/components/LayersWrapper.tsx | 21 +- .../DataLayerManagerComponent.tsx | 6 +- .../LayerFramework/framework/Group/Group.ts | 5 +- .../SettingManager/SettingManager.ts | 14 +- .../LayerFramework/groups/GroupRegistry.ts | 10 +- .../LayerFramework/groups/groupTypes.ts | 3 + .../groups/registerAllGroups.ts | 3 +- .../interfacesAndTypes/serialization.ts | 7 +- .../implementations/RealizationGridLayer.ts | 8 +- .../implementations/SensitivitySetting.tsx | 6 +- .../visualization/VisualizationFactory.ts | 123 ++++++++--- .../annotations/makeColorScaleAnnotation.ts | 11 +- ...eRealizationIntersectionGridBoundingBox.ts | 52 +++++ .../deckgl/makeRealizationGridLayer.ts | 32 ++- .../colorLegendsContainer.tsx | 24 ++- .../PreviewLayer/PreviewLayer.ts | 20 +- .../PreviewLayer/_private/BoxLayer.ts | 198 ++++++++++++++++++ .../PreviewLayer/_private/RectangleLayer.ts | 105 ---------- .../WellsLayer/_private/PipeLayer.ts | 6 +- 26 files changed, 514 insertions(+), 204 deletions(-) create mode 100644 frontend/src/modules/_shared/LayerFramework/groups/groupTypes.ts rename frontend/src/modules/{2DViewer/LayerFramework => _shared/LayerFramework/visualization/deckgl}/annotations/makeColorScaleAnnotation.ts (73%) create mode 100644 frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationIntersectionGridBoundingBox.ts create mode 100644 frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/BoxLayer.ts delete mode 100644 frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/RectangleLayer.ts diff --git a/backend_py/primary/primary/services/sumo_access/grid3d_access.py b/backend_py/primary/primary/services/sumo_access/grid3d_access.py index 81416197c..9feb53cb4 100644 --- a/backend_py/primary/primary/services/sumo_access/grid3d_access.py +++ b/backend_py/primary/primary/services/sumo_access/grid3d_access.py @@ -110,8 +110,8 @@ async def _get_grid_model_meta_async(search_context: SearchContext, item_no: int subgrids = [] dimensions = Grid3dDimensions( - i_count=grid_metadata["data"]["spec"]["nrow"], - j_count=grid_metadata["data"]["spec"]["ncol"], + i_count=grid_metadata["data"]["spec"]["ncol"], + j_count=grid_metadata["data"]["spec"]["nrow"], k_count=grid_metadata["data"]["spec"]["nlay"], subgrids=subgrids, ) diff --git a/frontend/src/lib/utils/geometry.ts b/frontend/src/lib/utils/geometry.ts index 742d7ccf2..97b981b50 100644 --- a/frontend/src/lib/utils/geometry.ts +++ b/frontend/src/lib/utils/geometry.ts @@ -7,6 +7,12 @@ export type Size2D = { height: number; }; +export type Size3D = { + width: number; + height: number; + depth: number; +}; + export type Rect2D = { x: number; y: number; @@ -24,13 +30,13 @@ export type Rect3D = { }; export enum ShapeType { - RECTANGLE = "rectangle", + BOX = "box", } export type Shape = { - type: ShapeType.RECTANGLE; + type: ShapeType.BOX; centerPoint: Vec3; - dimensions: Size2D; + dimensions: Size3D; normalizedEdgeVectors: { // along width u: Vec3; diff --git a/frontend/src/lib/utils/vec3.ts b/frontend/src/lib/utils/vec3.ts index 3beaa69a6..41cbd8e37 100644 --- a/frontend/src/lib/utils/vec3.ts +++ b/frontend/src/lib/utils/vec3.ts @@ -118,6 +118,16 @@ export function add(vector1: Vec3, vector2: Vec3): Vec3 { return { x: vector1.x + vector2.x, y: vector1.y + vector2.y, z: vector1.z + vector2.z }; } +/** + * Concatenates multiple vectors. + * + * @param vectors The vectors to concatenate. + * @returns A new vector that is the result of the concatenation. + */ +export function concat(...vectors: Vec3[]): Vec3 { + return vectors.reduce((acc, vector) => add(acc, vector), create(0, 0, 0)); +} + /** * Normalizes the given vector. * @@ -234,4 +244,4 @@ export function transform(vector: Vec3, matrix: Mat3): Vec3 { y: matrix.m10 * vector.x + matrix.m11 * vector.y + matrix.m12 * vector.z, z: matrix.m20 * vector.x + matrix.m21 * vector.y + matrix.m22 * vector.z, }; -} \ No newline at end of file +} diff --git a/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx index 70755349f..30ae875d8 100644 --- a/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/2DViewer/settings/components/layerManagerComponentWrapper.tsx @@ -20,6 +20,7 @@ import { Group } from "@modules/_shared/LayerFramework/framework/Group/Group"; import { SettingsGroup } from "@modules/_shared/LayerFramework/framework/SettingsGroup/SettingsGroup"; import { SharedSetting } from "@modules/_shared/LayerFramework/framework/SharedSetting/SharedSetting"; import { GroupRegistry } from "@modules/_shared/LayerFramework/groups/GroupRegistry"; +import { GroupType } from "@modules/_shared/LayerFramework/groups/groupTypes"; import type { Item, ItemGroup } from "@modules/_shared/LayerFramework/interfacesAndTypes/entitites"; import { instanceofItemGroup } from "@modules/_shared/LayerFramework/interfacesAndTypes/entitites"; import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; @@ -59,7 +60,9 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper function handleLayerAction(identifier: string, groupDelegate: GroupDelegate) { switch (identifier) { case "view": - groupDelegate.appendChild(GroupRegistry.makeGroup("View", props.layerManager, colorSet.getNextColor())); + groupDelegate.appendChild( + GroupRegistry.makeGroup(GroupType.VIEW, props.layerManager, colorSet.getNextColor()), + ); return; case "delta-surface": groupDelegate.appendChild(new DeltaSurface("Delta surface", props.layerManager)); @@ -143,7 +146,8 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper } const hasView = - groupDelegate.getDescendantItems((item) => item instanceof Group && item.getGroupType() === "View").length > 0; + groupDelegate.getDescendantItems((item) => item instanceof Group && item.getGroupType() === GroupType.VIEW) + .length > 0; const adjustedLayerActions = hasView ? LAYER_ACTIONS : INITIAL_LAYER_ACTIONS; return ( diff --git a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx index 8a3691806..502db588c 100644 --- a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx @@ -6,11 +6,12 @@ import { useViewStatusWriter } from "@framework/StatusWriter"; import { PendingWrapper } from "@lib/components/PendingWrapper"; import { useElementSize } from "@lib/hooks/useElementSize"; import * as bbox from "@lib/utils/bbox"; -import { makeColorScaleAnnotation } from "@modules/2DViewer/LayerFramework/annotations/makeColorScaleAnnotation"; import type { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; import type { DataLayerManager } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; import { LayerManagerTopic } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; +import { GroupType } from "@modules/_shared/LayerFramework/groups/groupTypes"; +import { View } from "@modules/_shared/LayerFramework/groups/implementations/View"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; import { ObservedSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/ObservedSurfaceLayer"; @@ -25,6 +26,7 @@ import type { VisualizationTarget, } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; import { VisualizationFactory } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import { makeColorScaleAnnotation } from "@modules/_shared/LayerFramework/visualization/deckgl/annotations/makeColorScaleAnnotation"; import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellTrajectoriesBoundingBox"; import { makeDrilledWellborePicksBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellborePicksBoundingBox"; import { makePolygonDataBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makePolygonDataBoundingBox"; @@ -88,6 +90,8 @@ VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.DRILLED_WELL_TRAJECTORIES calculateBoundingBoxFunction: makeDrilledWellTrajectoriesBoundingBox, }); +VISUALIZATION_FACTORY.registerViewFunction(GroupType.VIEW, View, () => ({ test: "test" })); + export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { const [prevBoundingBox, setPrevBoundingBox] = React.useState(null); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts index f6bb4901d..e75f92fe2 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -64,11 +64,12 @@ function predictDepthSliceGeometry({ const geometry: Geometry = { shapes: [ { - type: ShapeType.RECTANGLE, + type: ShapeType.BOX, centerPoint: vec3.create((origin.x + rotatedMaxXY.x) / 2, (origin.y + rotatedMaxXY.y) / 2, zmin), dimensions: { width: Math.abs(rotatedMaxX.x - origin.x), height: Math.abs(rotatedMaxY.y - origin.y), + depth: 0, }, normalizedEdgeVectors: { u: vec3.normalize(vec3.create(rotatedMaxX.x - origin.x, rotatedMaxX.y - origin.y, 0)), @@ -122,7 +123,7 @@ function predictCrosslineGeometry({ const geometry: Geometry = { shapes: [ { - type: ShapeType.RECTANGLE, + type: ShapeType.BOX, centerPoint: vec3.create( (rotatedMinXY.x + rotatedMaxXY.x) / 2, (rotatedMinXY.y + rotatedMaxXY.y) / 2, @@ -133,6 +134,7 @@ function predictCrosslineGeometry({ vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0), ), height: Math.abs(zmax - zmin), + depth: 0, }, normalizedEdgeVectors: { u: vec3.normalize(vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0)), @@ -189,7 +191,7 @@ function predictInlineGeometry({ const geometry: Geometry = { shapes: [ { - type: ShapeType.RECTANGLE, + type: ShapeType.BOX, centerPoint: vec3.create( (rotatedMinXY.x + rotatedMaxXY.x) / 2, (rotatedMinXY.y + rotatedMaxXY.y) / 2, @@ -200,6 +202,7 @@ function predictInlineGeometry({ vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0), ), height: Math.abs(zmax - zmin), + depth: 0, }, normalizedEdgeVectors: { u: vec3.normalize(vec3.create(rotatedMaxXY.x - rotatedMinXY.x, rotatedMaxXY.y - rotatedMinXY.y, 0)), diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index f312a61f2..b1dac82ab 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -19,6 +19,7 @@ import { Group } from "@modules/_shared/LayerFramework/framework/Group/Group"; import { SettingsGroup } from "@modules/_shared/LayerFramework/framework/SettingsGroup/SettingsGroup"; import { SharedSetting } from "@modules/_shared/LayerFramework/framework/SharedSetting/SharedSetting"; import { GroupRegistry } from "@modules/_shared/LayerFramework/groups/GroupRegistry"; +import { GroupType } from "@modules/_shared/LayerFramework/groups/groupTypes"; import { type Item, type ItemGroup, @@ -61,7 +62,9 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper function handleLayerAction(identifier: string, groupDelegate: GroupDelegate) { switch (identifier) { case "view": - groupDelegate.appendChild(GroupRegistry.makeGroup("View", props.layerManager, colorSet.getNextColor())); + groupDelegate.appendChild( + GroupRegistry.makeGroup(GroupType.VIEW, props.layerManager, colorSet.getNextColor()), + ); return; case "delta-surface": groupDelegate.appendChild(new DeltaSurface("Delta surface", props.layerManager)); @@ -95,6 +98,11 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper LayerRegistry.makeLayer(LayerType.DRILLED_WELLBORE_PICKS, props.layerManager), ); return; + case "intersection-realization-grid": + groupDelegate.prependChild( + LayerRegistry.makeLayer(LayerType.INTERSECTION_REALIZATION_GRID, props.layerManager), + ); + return; case "realization-grid": groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_GRID, props.layerManager)); return; @@ -157,7 +165,8 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper } const hasView = - groupDelegate.getDescendantItems((item) => item instanceof Group && item.getGroupType() === "View").length > 0; + groupDelegate.getDescendantItems((item) => item instanceof Group && item.getGroupType() === GroupType.VIEW) + .length > 0; const adjustedLayerActions = hasView ? LAYER_ACTIONS : INITIAL_LAYER_ACTIONS; return ( diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index c238063d1..9761116c3 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -7,13 +7,13 @@ import type { WorkbenchSession } from "@framework/WorkbenchSession"; import type { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { useElementSize } from "@lib/hooks/useElementSize"; import * as bbox from "@lib/utils/boundingBox"; -import { makeColorScaleAnnotation } from "@modules/2DViewer/LayerFramework/annotations/makeColorScaleAnnotation"; import type { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer"; import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; import { makeDrilledWellTrajectoriesLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer"; +import { makeIntersectionLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer"; import { makeRealizationSurfaceLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer"; import { Plane, @@ -23,8 +23,11 @@ import { type DataLayerManager, LayerManagerTopic, } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; +import { GroupType } from "@modules/_shared/LayerFramework/groups/groupTypes"; +import { View } from "@modules/_shared/LayerFramework/groups/implementations/View"; import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; +import { IntersectionRealizationGridLayer } from "@modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer"; import { RealizationGridLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer"; import { RealizationPolygonsLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationPolygonsLayer"; import { RealizationSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer"; @@ -36,11 +39,10 @@ import { VisualizationFactory, type VisualizationTarget, } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import { makeColorScaleAnnotation } from "@modules/_shared/LayerFramework/visualization/deckgl/annotations/makeColorScaleAnnotation"; import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellTrajectoriesBoundingBox"; import { makeDrilledWellborePicksBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellborePicksBoundingBox"; import { makePolygonDataBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makePolygonDataBoundingBox"; -import { makeRealizationGridBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationGridBoundingBox"; -import { makeSurfaceLayerBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeSurfaceLayerBoundingBox"; import { makeDrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellborePicksLayer"; import { makeRealizationGridLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer"; import { makeRealizationPolygonsLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationPolygonsLayer"; @@ -58,21 +60,26 @@ const VISUALIZATION_FACTORY = new VisualizationFactory ({ test: "test" })); + export type LayersWrapperProps = { layerManager: DataLayerManager; preferredViewLayout: PreferredViewLayout; diff --git a/frontend/src/modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManagerComponent.tsx b/frontend/src/modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManagerComponent.tsx index 0784fb9a6..91e50f0d5 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManagerComponent.tsx +++ b/frontend/src/modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManagerComponent.tsx @@ -4,17 +4,17 @@ import type { IsMoveAllowedArgs } from "@lib/components/SortableList"; import { SortableList } from "@lib/components/SortableList"; import { useElementSize } from "@lib/hooks/useElementSize"; import { convertRemToPixels } from "@lib/utils/screenUnitConversions"; -import type { GroupDelegate} from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; +import type { GroupDelegate } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; import { GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { Add } from "@mui/icons-material"; import type { DataLayerManager } from "./DataLayerManager"; -import type { LayersActionGroup} from "../../LayersActions"; +import type { LayersActionGroup } from "../../LayersActions"; import { LayersActions } from "../../LayersActions"; import { View } from "../../groups/implementations/View"; -import type { Item, ItemGroup} from "../../interfacesAndTypes/entitites"; +import type { Item, ItemGroup } from "../../interfacesAndTypes/entitites"; import { instanceofItemGroup } from "../../interfacesAndTypes/entitites"; import { SharedSetting } from "../SharedSetting/SharedSetting"; import { ExpandCollapseAllButton } from "../utilityComponents/ExpandCollapseAllButton"; diff --git a/frontend/src/modules/_shared/LayerFramework/framework/Group/Group.ts b/frontend/src/modules/_shared/LayerFramework/framework/Group/Group.ts index 908c44b51..f1ff3eeaa 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/Group/Group.ts +++ b/frontend/src/modules/_shared/LayerFramework/framework/Group/Group.ts @@ -1,6 +1,7 @@ import { GroupDelegate } from "../../delegates/GroupDelegate"; import { ItemDelegate } from "../../delegates/ItemDelegate"; import { SharedSettingsDelegate } from "../../delegates/SharedSettingsDelegate"; +import type { GroupType } from "../../groups/groupTypes"; import type { CustomGroupImplementation, CustomGroupImplementationWithSettings, @@ -21,7 +22,7 @@ export type GroupParams< > = { layerManager: DataLayerManager; color?: string; - type: string; + type: GroupType; customGroupImplementation: | CustomGroupImplementation | CustomGroupImplementationWithSettings; @@ -35,7 +36,7 @@ export class Group< { private _itemDelegate: ItemDelegate; private _groupDelegate: GroupDelegate; - private _type: string; + private _type: GroupType; private _sharedSettingsDelegate: SharedSettingsDelegate | null = null; constructor(params: GroupParams) { diff --git a/frontend/src/modules/_shared/LayerFramework/framework/SettingManager/SettingManager.ts b/frontend/src/modules/_shared/LayerFramework/framework/SettingManager/SettingManager.ts index 4f3341154..2c9f4f202 100644 --- a/frontend/src/modules/_shared/LayerFramework/framework/SettingManager/SettingManager.ts +++ b/frontend/src/modules/_shared/LayerFramework/framework/SettingManager/SettingManager.ts @@ -38,7 +38,7 @@ export type SettingTopicPayloads = { export type SettingManagerParams< TSetting extends Setting, TValue extends SettingTypes[TSetting] | null, - TCategory extends SettingCategories[TSetting] + TCategory extends SettingCategories[TSetting], > = { type: TSetting; category: TCategory; @@ -61,7 +61,7 @@ export enum OverriddenValueProviderType { export class SettingManager< TSetting extends Setting, TValue extends SettingTypes[TSetting] = SettingTypes[TSetting], - TCategory extends SettingCategories[TSetting] = SettingCategories[TSetting] + TCategory extends SettingCategories[TSetting] = SettingCategories[TSetting], > implements PublishSubscribe> { private _id: string; @@ -207,7 +207,7 @@ export class SettingManager< valueToRepresentation( value: TValue, workbenchSession: WorkbenchSession, - workbenchSettings: WorkbenchSettings + workbenchSettings: WorkbenchSettings, ): React.ReactNode { if (this._customSettingImplementation.overriddenValueRepresentation) { return this._customSettingImplementation.overriddenValueRepresentation({ @@ -342,12 +342,12 @@ export class SettingManager< if (customIsValueValidFunction) { isPersistedValueValid = customIsValueValidFunction( this._currentValueFromPersistence, - this._availableValues + this._availableValues, ); } else { isPersistedValueValid = settingCategoryIsValueValidMap[this._category]( this._currentValueFromPersistence as any, - this._availableValues as any + this._availableValues as any, ); } @@ -405,7 +405,7 @@ export class SettingManager< } else { candidate = settingCategoryFixupMap[this._category]( this._value as any, - this._availableValues as any + this._availableValues as any, ) as TValue; } @@ -417,9 +417,11 @@ export class SettingManager< } private checkIfValueIsValid(value: TValue): boolean { + /* if (value === null) { return false; } + */ if (this._isStatic) { return true; } diff --git a/frontend/src/modules/_shared/LayerFramework/groups/GroupRegistry.ts b/frontend/src/modules/_shared/LayerFramework/groups/GroupRegistry.ts index a4be26b1e..ef2ed49c6 100644 --- a/frontend/src/modules/_shared/LayerFramework/groups/GroupRegistry.ts +++ b/frontend/src/modules/_shared/LayerFramework/groups/GroupRegistry.ts @@ -1,10 +1,12 @@ +import type { GroupType } from "./groupTypes"; + import type { DataLayerManager } from "../framework/DataLayerManager/DataLayerManager"; import { Group } from "../framework/Group/Group"; import type { CustomGroupImplementation } from "../interfacesAndTypes/customGroupImplementation"; export class GroupRegistry { private static _registeredGroups: Map< - string, + GroupType, { group: { new (customParams?: any): CustomGroupImplementation }; customParams?: any; @@ -12,9 +14,9 @@ export class GroupRegistry { > = new Map(); static registerGroup( - name: string, + name: GroupType, group: TGroup, - customParams?: ConstructorParameters + customParams?: ConstructorParameters, ): void { if (this._registeredGroups.has(name)) { throw new Error(`Group ${name} already registered`); @@ -25,7 +27,7 @@ export class GroupRegistry { }); } - static makeGroup(type: string, layerManager: DataLayerManager, color?: string): Group { + static makeGroup(type: GroupType, layerManager: DataLayerManager, color?: string): Group { const stored = this._registeredGroups.get(type); if (!stored) { throw new Error(`Group ${type} not found`); diff --git a/frontend/src/modules/_shared/LayerFramework/groups/groupTypes.ts b/frontend/src/modules/_shared/LayerFramework/groups/groupTypes.ts new file mode 100644 index 000000000..9a7ae4eb6 --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/groups/groupTypes.ts @@ -0,0 +1,3 @@ +export enum GroupType { + VIEW = "VIEW", +} diff --git a/frontend/src/modules/_shared/LayerFramework/groups/registerAllGroups.ts b/frontend/src/modules/_shared/LayerFramework/groups/registerAllGroups.ts index a26ec3577..f70d3a52d 100644 --- a/frontend/src/modules/_shared/LayerFramework/groups/registerAllGroups.ts +++ b/frontend/src/modules/_shared/LayerFramework/groups/registerAllGroups.ts @@ -1,4 +1,5 @@ import { GroupRegistry } from "./GroupRegistry"; +import { GroupType } from "./groupTypes"; import { View } from "./implementations/View"; -GroupRegistry.registerGroup("View", View); +GroupRegistry.registerGroup(GroupType.VIEW, View); diff --git a/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/serialization.ts b/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/serialization.ts index 50640c0df..03d53424a 100644 --- a/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/serialization.ts +++ b/frontend/src/modules/_shared/LayerFramework/interfacesAndTypes/serialization.ts @@ -2,6 +2,7 @@ import type { ColorScaleSerialization } from "@lib/utils/ColorScale"; import type { SettingsKeysFromTuple } from "./utils"; +import type { GroupType } from "../groups/groupTypes"; import type { Setting, Settings } from "../settings/settingsDefinitions"; // The following interfaces/types are used to define the structure of the serialized state of the respective items in the data layer framework. @@ -26,14 +27,14 @@ export interface SerializedItem { export type SerializedSettingsState< TSettings extends Settings, - TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple + TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, > = { [K in TSettingKey]: string; }; export interface SerializedLayer< TSettings extends Settings, - TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple + TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, > extends SerializedItem { type: SerializedType.LAYER; layerType: string; @@ -42,7 +43,7 @@ export interface SerializedLayer< export interface SerializedGroup extends SerializedItem { type: SerializedType.GROUP; - groupType: string; + groupType: GroupType; color: string; children: SerializedItem[]; } diff --git a/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer.ts b/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer.ts index 3bee3c975..ee0ae7672 100644 --- a/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer.ts @@ -111,9 +111,9 @@ export class RealizationGridLayer parameter_time_or_interval_str: timeOrInterval, realization_num: realizationNum ?? 0, i_min: iMin, - i_max: iMax - 1, + i_max: iMax, j_min: jMin, - j_max: jMax - 1, + j_max: jMax, k_min: kMin, k_max: kMax, }, @@ -130,9 +130,9 @@ export class RealizationGridLayer grid_name: gridName ?? "", realization_num: realizationNum ?? 0, i_min: iMin, - i_max: iMax - 1, + i_max: iMax, j_min: jMin, - j_max: jMax - 1, + j_max: jMax, k_min: kMin, k_max: kMax, }, diff --git a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SensitivitySetting.tsx b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SensitivitySetting.tsx index 6a59608e1..a59bb03cc 100644 --- a/frontend/src/modules/_shared/LayerFramework/settings/implementations/SensitivitySetting.tsx +++ b/frontend/src/modules/_shared/LayerFramework/settings/implementations/SensitivitySetting.tsx @@ -18,7 +18,7 @@ type ValueType = SensitivityNameCasePair | null; export class SensitivitySetting implements CustomSettingImplementation { isValueValid( value: ValueType, - availableValues: MakeAvailableValuesTypeBasedOnCategory + availableValues: MakeAvailableValuesTypeBasedOnCategory, ): boolean { if (availableValues.length === 0) { return true; @@ -31,7 +31,7 @@ export class SensitivitySetting implements CustomSettingImplementation sensitivity?.sensitivityName === value.sensitivityName && - sensitivity?.sensitivityCase === value.sensitivityCase + sensitivity?.sensitivityCase === value.sensitivityCase, ); } @@ -50,7 +50,7 @@ export class SensitivitySetting implements CustomSettingImplementation ({ diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts index 5e3db582a..8c33b2332 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/VisualizationFactory.ts @@ -14,9 +14,15 @@ import type { CustomDataLayerImplementation, DataLayerInformationAccessors, } from "../interfacesAndTypes/customDataLayerImplementation"; +import type { + CustomGroupImplementation, + CustomGroupImplementationWithSettings, +} from "../interfacesAndTypes/customGroupImplementation"; import { instanceofItemGroup } from "../interfacesAndTypes/entitites"; import type { StoredData } from "../interfacesAndTypes/sharedTypes"; -import type { Settings } from "../settings/settingsDefinitions"; +import type { SettingsKeysFromTuple } from "../interfacesAndTypes/utils"; +import type { IntersectionSettingValue } from "../settings/implementations/IntersectionSetting"; +import type { SettingTypes, Settings } from "../settings/settingsDefinitions"; export enum VisualizationTarget { DECK_GL = "deck_gl", @@ -34,9 +40,40 @@ export type FactoryFunctionArgs< name: string; isLoading: boolean; getInjectedData: () => TInjectedData; + getValueRange: () => [number, number] | null; +}; + +export type VisualizationViewBasic = { + id: string; + color: string | null; + name: string; + layers: LayerWithPosition[]; + annotations: Annotation[]; +}; + +export type EsvView = { + intersection: IntersectionSettingValue; + extensionLength: number; }; -export type TargetReturnTypes = { +export type TargetViewReturnTypes = { + [VisualizationTarget.DECK_GL]: {}; + [VisualizationTarget.ESV]: EsvView; +}; + +export interface ViewDataCollectorFunction< + TSettings extends Settings, + TTarget extends VisualizationTarget, + TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, +> { + (args: { + id: string; + name: string; + getSetting: (setting: TKey) => SettingTypes[TKey]; + }): TargetViewReturnTypes[TTarget]; +} + +export type TargetLayerReturnTypes = { [VisualizationTarget.DECK_GL]: DeckGlLayer; [VisualizationTarget.ESV]: EsvLayer; }; @@ -76,7 +113,7 @@ export type MakeVisualizationFunction< TTarget extends VisualizationTarget, TStoredData extends StoredData = Record, TInjectedData extends Record = never, -> = (args: FactoryFunctionArgs) => TargetReturnTypes[TTarget] | null; +> = (args: FactoryFunctionArgs) => TargetLayerReturnTypes[TTarget] | null; // This does likely require a refactor as soon as we have tested against a use case export type MakeHoverVisualizationFunction< @@ -89,7 +126,7 @@ export type MakeHoverVisualizationFunction< args: FactoryFunctionArgs & { hoverInfo: Partial; }, -) => TargetReturnTypes[TTarget][]; +) => TargetLayerReturnTypes[TTarget][]; export type CalculateBoundingBoxFunction< TSettings extends Settings, @@ -117,30 +154,22 @@ export type ReduceAccumulatedDataFunction< ) => TAccumulatedData; export type LayerWithPosition = { - layer: TargetReturnTypes[TTarget]; + layer: TargetLayerReturnTypes[TTarget]; position: number; }; -export type VisualizationView = { - id: string; - color: string | null; - name: string; - layers: LayerWithPosition[]; - annotations: Annotation[]; -}; - export type FactoryProduct< TTarget extends VisualizationTarget, TAccumulatedData extends Record = never, > = { - views: VisualizationView[]; + views: (VisualizationViewBasic & TargetViewReturnTypes[TTarget])[]; layers: LayerWithPosition[]; errorMessages: (StatusMessage | string)[]; combinedBoundingBox: bbox.BBox | null; numLoadingLayers: number; annotations: Annotation[]; accumulatedData: TAccumulatedData; - makeHoverVisualizationsFunction: (hoverInfo: Partial) => TargetReturnTypes[TTarget][]; + makeHoverVisualizationsFunction: (hoverInfo: Partial) => TargetLayerReturnTypes[TTarget][]; }; export class VisualizationFactory< @@ -153,6 +182,8 @@ export class VisualizationFactory< LayerVisualizationFunctions > = new Map(); + private _viewFunctions: Map> = new Map(); + registerLayerFunctions>( layerName: string, layerCtor: { @@ -166,6 +197,19 @@ export class VisualizationFactory< this._visualizationFunctions.set(layerName, funcs); } + registerViewFunction( + viewName: string, + viewCtor: { + new (...params: any[]): CustomGroupImplementation | CustomGroupImplementationWithSettings; + }, + func: ViewDataCollectorFunction, + ): void { + if (this._visualizationFunctions.has(viewCtor.name)) { + throw new Error(`Visualization function for view ${viewCtor.name} already registered`); + } + this._viewFunctions.set(viewName, func); + } + make( layerManager: DataLayerManager, options?: { @@ -186,13 +230,13 @@ export class VisualizationFactory< injectedData?: TInjectedData, numCollectedLayers: number = 0, ): FactoryProduct { - const collectedViews: VisualizationView[] = []; + const collectedViews: (VisualizationViewBasic & TargetViewReturnTypes[TTarget])[] = []; const collectedLayers: LayerWithPosition[] = []; const collectedAnnotations: Annotation[] = []; const collectedErrorMessages: (StatusMessage | string)[] = []; const collectedMakeHoverVisualizationFunctions: (( hoverInfo: Partial, - ) => TargetReturnTypes[TTarget][])[] = []; + ) => TargetLayerReturnTypes[TTarget][])[] = []; let collectedNumLoadingLayers = 0; let globalBoundingBox: bbox.BBox | null = null; @@ -235,13 +279,7 @@ export class VisualizationFactory< maybeApplyBoundingBox(boundingBox); if (child instanceof Group) { - const view: VisualizationView = { - id: child.getItemDelegate().getId(), - color: child.getGroupDelegate().getColor(), - name: child.getItemDelegate().getName(), - layers, - annotations, - }; + const view = this.makeView(child, layers, annotations); collectedViews.push(view); continue; @@ -292,7 +330,7 @@ export class VisualizationFactory< numLoadingLayers: collectedNumLoadingLayers, accumulatedData, makeHoverVisualizationsFunction: (hoverInfo: Partial) => { - const collectedHoverVisualizations: TargetReturnTypes[TTarget][] = []; + const collectedHoverVisualizations: TargetLayerReturnTypes[TTarget][] = []; for (const makeHoverVisualizationFunction of collectedMakeHoverVisualizationFunctions) { collectedHoverVisualizations.push(...makeHoverVisualizationFunction(hoverInfo)); } @@ -301,6 +339,36 @@ export class VisualizationFactory< }; } + private makeView< + TSettings extends Settings, + TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, + >( + group: Group, + layers: LayerWithPosition[], + annotations: Annotation[], + ): VisualizationViewBasic & TargetViewReturnTypes[TTarget] { + const func = this._viewFunctions.get(group.getGroupType()); + if (!func) { + throw new Error( + `No view function provided for group ${group.getGroupType()}. Did you forget to register it?`, + ); + } + + return { + id: group.getItemDelegate().getId(), + color: group.getGroupDelegate().getColor(), + name: group.getItemDelegate().getName(), + layers, + annotations, + ...func({ + id: group.getItemDelegate().getId(), + name: group.getItemDelegate().getName(), + getSetting: (setting: TKey) => + group.getSharedSettingsDelegate()?.getWrappedSettings()[setting], + }), + }; + } + private makeFactoryFunctionArgs< TSettings extends Settings, TData, @@ -321,6 +389,7 @@ export class VisualizationFactory< name: layer.getItemDelegate().getName(), isLoading: layer.getStatus() === DataLayerStatus.LOADING, getInjectedData: getInjectedData.bind(this), + getValueRange: layer.getValueRange.bind(layer), ...layer.makeAccessors(), }; } @@ -328,7 +397,7 @@ export class VisualizationFactory< private makeLayer( layer: DataLayer, injectedData?: TInjectedData, - ): TargetReturnTypes[TTarget] | null { + ): TargetLayerReturnTypes[TTarget] | null { const func = this._visualizationFunctions.get(layer.getType())?.makeVisualizationFunction; if (!func) { throw new Error(`No visualization function found for layer ${layer.getType()}`); @@ -340,7 +409,7 @@ export class VisualizationFactory< private makeHoverLayerFunction( layer: DataLayer, injectedData?: TInjectedData, - ): (hoverInfo: Partial) => TargetReturnTypes[TTarget][] { + ): (hoverInfo: Partial) => TargetLayerReturnTypes[TTarget][] { const func = this._visualizationFunctions.get(layer.getType())?.makeHoverVisualizationFunction; if (!func) { return () => []; diff --git a/frontend/src/modules/2DViewer/LayerFramework/annotations/makeColorScaleAnnotation.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/annotations/makeColorScaleAnnotation.ts similarity index 73% rename from frontend/src/modules/2DViewer/LayerFramework/annotations/makeColorScaleAnnotation.ts rename to frontend/src/modules/_shared/LayerFramework/visualization/deckgl/annotations/makeColorScaleAnnotation.ts index 69435236a..b780f9800 100644 --- a/frontend/src/modules/2DViewer/LayerFramework/annotations/makeColorScaleAnnotation.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/annotations/makeColorScaleAnnotation.ts @@ -9,6 +9,7 @@ export function makeColorScaleAnnotation({ getSetting, id, name, + getValueRange, }: FactoryFunctionArgs<[Setting.COLOR_SCALE], any>): Annotation[] { const colorScale = getSetting(Setting.COLOR_SCALE)?.colorScale; @@ -16,5 +17,13 @@ export function makeColorScaleAnnotation({ return []; } - return [{ id, colorScale: ColorScaleWithName.fromColorScale(colorScale, name) }]; + const range = getValueRange(); + if (!range) { + return []; + } + + const localColorScale = colorScale.clone(); + localColorScale.setRange(range[0], range[1]); + + return [{ id, colorScale: ColorScaleWithName.fromColorScale(localColorScale, name) }]; } diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationIntersectionGridBoundingBox.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationIntersectionGridBoundingBox.ts new file mode 100644 index 000000000..98df21f15 --- /dev/null +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeRealizationIntersectionGridBoundingBox.ts @@ -0,0 +1,52 @@ +import * as bbox from "@lib/utils/bbox"; +import type { IntersectionRealizationGridSettings } from "@modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer"; +import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; +import type { PolylineIntersection_trans } from "@modules/_shared/utils/wellbore"; + +export function makeRealizationIntersectionBoundingBox({ + getData, +}: FactoryFunctionArgs): bbox.BBox | null { + const data = getData(); + if (!data) { + return null; + } + + let boundingBox: bbox.BBox = bbox.create( + { + x: 0, + y: 0, + z: 0, + }, + { + x: 0, + y: 0, + z: 0, + }, + ); + + for (const section of data.fenceMeshSections) { + let minZ = Number.MAX_VALUE; + let maxZ = Number.MIN_VALUE; + for (const vertex of section.verticesUzFloat32Arr) { + minZ = Math.min(minZ, vertex); + maxZ = Math.max(maxZ, vertex); + } + boundingBox = bbox.combine( + boundingBox, + bbox.create( + { + x: section.start_utm_x, + y: section.start_utm_y, + z: minZ, + }, + { + x: section.end_utm_x, + y: section.end_utm_y, + z: maxZ, + }, + ), + ); + } + + return boundingBox; +} diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer.ts index 576391834..0d11c861d 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer.ts @@ -1,22 +1,40 @@ +import { ColorPalette } from "@lib/utils/ColorPalette"; +import { ColorScale, ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; +import { PreviewLayer } from "@modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer"; import { Grid3DLayer } from "@webviz/subsurface-viewer/dist/layers"; +import { makeRealizationGridBoundingBox } from "./boundingBoxes/makeRealizationGridBoundingBox"; + import type { RealizationGridData, RealizationGridSettings } from "../../layers/implementations/RealizationGridLayer"; -export function makeRealizationGridLayer({ - id, - getData, - getSetting, -}: FactoryFunctionArgs): Grid3DLayer | null { +export function makeRealizationGridLayer( + args: FactoryFunctionArgs, +): Grid3DLayer | PreviewLayer | null { + const { id, getData, getSetting, isLoading } = args; const data = getData(); - const colorScale = getSetting(Setting.COLOR_SCALE)?.colorScale; + let colorScale = getSetting(Setting.COLOR_SCALE)?.colorScale; + const boundingBox = makeRealizationGridBoundingBox(args); - if (!data) { + if (!data || !boundingBox || !colorScale) { return null; } + if (isLoading) { + colorScale = new ColorScale({ + colorPalette: new ColorPalette({ + name: "ResInsight", + colors: ["#EEEEEE", "#EFEFEF"], + id: "black-white", + }), + gradientType: ColorScaleGradientType.Sequential, + type: ColorScaleType.Continuous, + steps: 100, + }); + } + const { gridSurfaceData, gridParameterData } = data; const showGridLines = getSetting(Setting.SHOW_GRID_LINES); diff --git a/frontend/src/modules/_shared/components/ColorLegendsContainer/colorLegendsContainer.tsx b/frontend/src/modules/_shared/components/ColorLegendsContainer/colorLegendsContainer.tsx index 9160a4c92..9a33a25e2 100644 --- a/frontend/src/modules/_shared/components/ColorLegendsContainer/colorLegendsContainer.tsx +++ b/frontend/src/modules/_shared/components/ColorLegendsContainer/colorLegendsContainer.tsx @@ -34,6 +34,7 @@ function makeMarkers( sectionBottom: number, left: number, barHeight: number, + maxNumLog10: number = 4, ): React.ReactNode[] { const sectionHeight = Math.abs(sectionBottom - sectionTop); @@ -72,7 +73,7 @@ function makeMarkers( fontSize="10" style={TEXT_STYLE} > - {formatLegendValue(value)} + {formatLegendValue(value, maxNumLog10)} , ); @@ -81,7 +82,13 @@ function makeMarkers( return markers; } -function makeDiscreteMarkers(colorScale: ColorScale, left: number, top: number, barHeight: number): React.ReactNode[] { +function makeDiscreteMarkers( + colorScale: ColorScale, + left: number, + top: number, + barHeight: number, + maxNumLog10: number = 4, +): React.ReactNode[] { const minMarkerHeight = STYLE_CONSTANTS.fontSize + 2 * STYLE_CONSTANTS.textGap; const numSteps = colorScale.getNumSteps(); @@ -123,7 +130,7 @@ function makeDiscreteMarkers(colorScale: ColorScale, left: number, top: number, fontSize="10" style={TEXT_STYLE} > - {formatLegendValue(value)} + {formatLegendValue(value, maxNumLog10)} , ); @@ -140,6 +147,7 @@ type ColorLegendProps = { left: number; totalHeight: number; barWidth: number; + maxNumLog10?: number; }; function ColorLegend(props: ColorLegendProps): React.ReactNode { @@ -170,7 +178,7 @@ function ColorLegend(props: ColorLegendProps): React.ReactNode { fontSize="10" style={TEXT_STYLE} > - {formatLegendValue(props.colorScale.getMax())} + {formatLegendValue(props.colorScale.getMax(), props.maxNumLog10)} , ); @@ -274,7 +282,7 @@ function ColorLegend(props: ColorLegendProps): React.ReactNode { fontSize="10" style={TEXT_STYLE} > - {formatLegendValue(props.colorScale.getMin())} + {formatLegendValue(props.colorScale.getMin(), props.maxNumLog10)} , ); @@ -327,6 +335,7 @@ export type ColorLegendsContainerProps = { colorScales: ColorScaleWithId[]; height: number; position?: "left" | "right"; + maxNumLog10?: number; }; export function ColorLegendsContainer(props: ColorLegendsContainerProps): React.ReactNode { @@ -369,6 +378,7 @@ export function ColorLegendsContainer(props: ColorLegendsContainerProps): React. left={left} totalHeight={height} barWidth={width} + maxNumLog10={props.maxNumLog10} />, ); } @@ -443,9 +453,9 @@ function countDecimalPlaces(value: number): number { return decimalIndex >= 0 ? value.toString().length - decimalIndex - 1 : 0; } -function formatLegendValue(value: number): string { +function formatLegendValue(value: number, maxNumLog10: number = 4): string { const numDecimalPlaces = countDecimalPlaces(value); - if (Math.log10(Math.abs(value)) > 2) { + if (Math.log10(Math.abs(value)) > maxNumLog10) { return value.toExponential(numDecimalPlaces > 2 ? 2 : numDecimalPlaces); } return value.toFixed(numDecimalPlaces > 2 ? 2 : numDecimalPlaces); diff --git a/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer.ts index 28beb30b2..c4d958110 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer.ts @@ -2,7 +2,7 @@ import { CompositeLayer, Layer, LayersList } from "@deck.gl/core"; import { Geometry, ShapeType } from "@lib/utils/geometry"; import * as vec3 from "@lib/utils/vec3"; -import { RectangleLayer } from "./_private/RectangleLayer"; +import { BoxLayer } from "./_private/BoxLayer"; export type PreviewLayerProps = { id: string; @@ -23,25 +23,29 @@ export class PreviewLayer extends CompositeLayer { const zFactor = this.props.zIncreaseDownwards ? -1 : 1; for (const [idx, shape] of data.geometry.shapes.entries()) { - if (shape.type === ShapeType.RECTANGLE) { + if (shape.type === ShapeType.BOX) { layers.push( - new RectangleLayer({ + new BoxLayer({ id: `${idx}`, data: { centerPoint: vec3.toArray( - vec3.multiplyElementWise(shape.centerPoint, vec3.create(1, 1, zFactor)) + vec3.multiplyElementWise(shape.centerPoint, vec3.create(1, 1, zFactor)), ), - dimensions: [shape.dimensions.width, shape.dimensions.height], + dimensions: [ + shape.dimensions.width, + shape.dimensions.height, + shape.dimensions.depth * zFactor, + ], normalizedEdgeVectors: [ vec3.toArray( - vec3.multiplyElementWise(shape.normalizedEdgeVectors.u, vec3.create(1, 1, zFactor)) + vec3.multiplyElementWise(shape.normalizedEdgeVectors.u, vec3.create(1, 1, zFactor)), ), vec3.toArray( - vec3.multiplyElementWise(shape.normalizedEdgeVectors.v, vec3.create(1, 1, zFactor)) + vec3.multiplyElementWise(shape.normalizedEdgeVectors.v, vec3.create(1, 1, zFactor)), ), ], }, - }) + }), ); } } diff --git a/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/BoxLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/BoxLayer.ts new file mode 100644 index 000000000..37302a313 --- /dev/null +++ b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/BoxLayer.ts @@ -0,0 +1,198 @@ +import { CompositeLayer, CompositeLayerProps, Layer, UpdateParameters } from "@deck.gl/core"; +import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; +import * as vec3 from "@lib/utils/vec3"; +import { Geometry } from "@luma.gl/engine"; + +export type RectangleLayerData = { + centerPoint: [number, number, number]; + dimensions: [number, number, number]; + normalizedEdgeVectors: [[number, number, number], [number, number, number]]; +}; + +export type RectangleLayerProps = { + id: string; + data: RectangleLayerData; +}; + +export class BoxLayer extends CompositeLayer { + static layerName = "BoxLayer"; + + // @ts-expect-error - private + state!: { + geometry: Geometry; + }; + + private makeGeometry(): Geometry { + const { data } = this.props; + + const vertices: Float32Array = new Float32Array(8 * 3); + const indices: Uint16Array = new Uint16Array(3 * 2 * 6); + + const [centerX, centerY, centerZ] = data.centerPoint; + const [width, height, depth] = data.dimensions; + const [[uX, uY, uZ], [vX, vY, vZ]] = data.normalizedEdgeVectors; + + const halfWidth = width / 2; + const halfHeight = height / 2; + const halfDepth = depth / 2; + + const center = vec3.fromArray([centerX, centerY, centerZ]); + const vecU = vec3.fromArray([uX, uY, uZ]); + const vecV = vec3.fromArray([vX, vY, vZ]); + + // Make normal vector from u and v + const vecW = vec3.cross(vecU, vecV); + + // Make vertices wrt to vectors + vertices.set( + vec3.toArray( + vec3.concat( + center, + vec3.scale(vecU, halfWidth), + vec3.scale(vecV, halfHeight), + vec3.scale(vecW, halfDepth), + ), + ), + 0, + ); + vertices.set( + vec3.toArray( + vec3.concat( + center, + vec3.scale(vecU, halfWidth), + vec3.scale(vecV, -halfHeight), + vec3.scale(vecW, halfDepth), + ), + ), + 3, + ); + vertices.set( + vec3.toArray( + vec3.concat( + center, + vec3.scale(vecU, -halfWidth), + vec3.scale(vecV, -halfHeight), + vec3.scale(vecW, halfDepth), + ), + ), + 6, + ); + vertices.set( + vec3.toArray( + vec3.concat( + center, + vec3.scale(vecU, -halfWidth), + vec3.scale(vecV, halfHeight), + vec3.scale(vecW, halfDepth), + ), + ), + 9, + ); + vertices.set( + vec3.toArray( + vec3.concat( + center, + vec3.scale(vecU, halfWidth), + vec3.scale(vecV, halfHeight), + vec3.scale(vecW, -halfDepth), + ), + ), + 12, + ); + vertices.set( + vec3.toArray( + vec3.concat( + center, + vec3.scale(vecU, halfWidth), + vec3.scale(vecV, -halfHeight), + vec3.scale(vecW, -halfDepth), + ), + ), + 15, + ); + vertices.set( + vec3.toArray( + vec3.concat( + center, + vec3.scale(vecU, -halfWidth), + vec3.scale(vecV, -halfHeight), + vec3.scale(vecW, -halfDepth), + ), + ), + 18, + ); + vertices.set( + vec3.toArray( + vec3.concat( + center, + vec3.scale(vecU, -halfWidth), + vec3.scale(vecV, halfHeight), + vec3.scale(vecW, -halfDepth), + ), + ), + 21, + ); + + // Front + indices.set([0, 1, 2], 0); + indices.set([0, 2, 3], 3); + + // Back + indices.set([4, 6, 5], 6); + indices.set([4, 7, 6], 9); + + // Left + indices.set([0, 7, 4], 12); + indices.set([0, 3, 7], 15); + + // Right + indices.set([1, 5, 6], 18); + indices.set([1, 6, 2], 21); + + // Top + indices.set([3, 2, 6], 24); + indices.set([3, 6, 7], 27); + + // Bottom + indices.set([0, 4, 5], 30); + indices.set([0, 5, 1], 33); + + return new Geometry({ + topology: "triangle-list", + attributes: { + positions: vertices, + }, + indices, + }); + } + + initializeState(): void { + this.setState({ + ...this.state, + isHovered: false, + isLoaded: false, + }); + } + + updateState({ changeFlags }: UpdateParameters>>) { + if (changeFlags.dataChanged) { + this.setState({ + geometry: this.makeGeometry(), + }); + } + } + + renderLayers() { + return [ + new SimpleMeshLayer({ + id: "mesh", + data: [0], + mesh: this.state.geometry, + getPosition: (d) => [0, 0, 0], + getColor: [100, 100, 100, 100], + material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, + pickable: false, + }), + ]; + } +} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/RectangleLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/RectangleLayer.ts deleted file mode 100644 index fac0a0823..000000000 --- a/frontend/src/modules/_shared/customDeckGlLayers/PreviewLayer/_private/RectangleLayer.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { CompositeLayer, CompositeLayerProps, Layer, UpdateParameters } from "@deck.gl/core"; -import { SimpleMeshLayer } from "@deck.gl/mesh-layers"; -import { Geometry } from "@luma.gl/engine"; - -export type RectangleLayerData = { - centerPoint: [number, number, number]; - dimensions: [number, number]; - normalizedEdgeVectors: [[number, number, number], [number, number, number]]; -}; - -export type RectangleLayerProps = { - id: string; - data: RectangleLayerData; -}; - -export class RectangleLayer extends CompositeLayer { - static layerName = "RectangleLayer"; - - // @ts-expect-error - private - state!: { - geometry: Geometry; - }; - - private makeGeometry(): Geometry { - const { data } = this.props; - - const vertices: Float32Array = new Float32Array(4 * 3); - const indices: Uint16Array = new Uint16Array(6); - - const [centerX, centerY, centerZ] = data.centerPoint; - const [width, height] = data.dimensions; - const [[uX, uY, uZ], [vX, vY, vZ]] = data.normalizedEdgeVectors; - - const halfWidth = width / 2; - const halfHeight = height / 2; - - // bottom left - vertices[0] = centerX - uX * halfWidth - vX * halfHeight; - vertices[1] = centerY - uY * halfWidth - vY * halfHeight; - vertices[2] = centerZ - uZ * halfWidth - vZ * halfHeight; - - // bottom right - vertices[3] = centerX + uX * halfWidth - vX * halfHeight; - vertices[4] = centerY + uY * halfWidth - vY * halfHeight; - vertices[5] = centerZ + uZ * halfWidth - vZ * halfHeight; - - // top right - vertices[6] = centerX + uX * halfWidth + vX * halfHeight; - vertices[7] = centerY + uY * halfWidth + vY * halfHeight; - vertices[8] = centerZ + uZ * halfWidth + vZ * halfHeight; - - // top left - vertices[9] = centerX - uX * halfWidth + vX * halfHeight; - vertices[10] = centerY - uY * halfWidth + vY * halfHeight; - vertices[11] = centerZ - uZ * halfWidth + vZ * halfHeight; - - // bottom left - indices[0] = 0; - indices[1] = 1; - indices[2] = 2; - - // top right - indices[3] = 2; - indices[4] = 3; - indices[5] = 0; - - return new Geometry({ - topology: "triangle-list", - attributes: { - positions: vertices, - }, - indices, - }); - } - - initializeState(): void { - this.setState({ - ...this.state, - isHovered: false, - isLoaded: false, - }); - } - - updateState({ changeFlags }: UpdateParameters>>) { - if (changeFlags.dataChanged) { - this.setState({ - geometry: this.makeGeometry(), - }); - } - } - - renderLayers() { - return [ - new SimpleMeshLayer({ - id: "mesh", - data: [0], - mesh: this.state.geometry, - getPosition: (d) => [0, 0, 0], - getColor: [100, 100, 100, 100], - material: { ambient: 0.95, diffuse: 1, shininess: 0, specularColor: [0, 0, 0] }, - pickable: false, - }), - ]; - } -} diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts index 1824f7350..f0ef63538 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts @@ -12,7 +12,7 @@ import { import { Vec3 } from "@lib/utils/vec3"; import * as vec3 from "@lib/utils/vec3"; import { Geometry, Model } from "@luma.gl/engine"; -import { phongLighting, phongMaterial } from "@luma.gl/shadertools"; +import { phongLighting } from "@luma.gl/shadertools"; import { isEqual } from "lodash"; @@ -90,7 +90,7 @@ export class PipeLayer extends Layer { updateState(params: UpdateParameters>): void { super.updateState(params); - if (!isEqual(this.props.data, params.props.data)) { + if (!isEqual(params.props.data, params.oldProps.data)) { this.setState({ models: this.makeModels(params.context), }); @@ -110,7 +110,7 @@ export class PipeLayer extends Layer { const model = new Model(context.device, { id: `${this.id}-mesh-${idx}`, geometry: mesh, - modules: [project32, phongLighting, picking, pipeUniforms, phongMaterial], + modules: [project32, phongLighting, picking, pipeUniforms], vs: vertexShader, fs: fragmentShader, }); From 8f6572996c5c072b30e22bf21beaa606db7374c6 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 31 Mar 2025 13:35:28 +0200 Subject: [PATCH 72/97] wip --- .../view/components/LayersWrapper.tsx | 2 +- .../makeDrilledWellTrajectoriesLayer.ts | 3 +- .../makeDrilledWellTrajectoriesLayer.ts | 2 + .../WellsLayer/WellsLayer.ts | 81 +++++++++++-------- .../WellsLayer/_private/PipeLayer.ts | 10 ++- 5 files changed, 63 insertions(+), 35 deletions(-) diff --git a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx index 502db588c..92c31391e 100644 --- a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx @@ -90,7 +90,7 @@ VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.DRILLED_WELL_TRAJECTORIES calculateBoundingBoxFunction: makeDrilledWellTrajectoriesBoundingBox, }); -VISUALIZATION_FACTORY.registerViewFunction(GroupType.VIEW, View, () => ({ test: "test" })); +VISUALIZATION_FACTORY.registerViewFunction(GroupType.VIEW, View, () => ({})); export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { const [prevBoundingBox, setPrevBoundingBox] = React.useState(null); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts index dc1f769fe..9e090547d 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts +++ b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts @@ -8,7 +8,7 @@ import type { WellsLayerData } from "@modules/_shared/customDeckGlLayers/WellsLa export function makeDrilledWellTrajectoriesLayer( args: FactoryFunctionArgs, ): WellsLayer | null { - const { id, getData } = args; + const { id, getData, name } = args; const fieldWellboreTrajectoriesData = getData(); @@ -37,6 +37,7 @@ export function makeDrilledWellTrajectoriesLayer( const wellsLayer = new WellsLayer({ id: id, + name, data: wellsLayerData, zIncreaseDownwards: true, boundingBox: bbox.toNumArray(boundingBox), diff --git a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts index 9b5988181..18c2a384e 100644 --- a/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts +++ b/frontend/src/modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts @@ -54,6 +54,7 @@ function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { export function makeDrilledWellTrajectoriesLayer({ id, + name, getData, }: FactoryFunctionArgs): WellsLayer | null { const fieldWellboreTrajectoriesData = getData(); @@ -92,6 +93,7 @@ export function makeDrilledWellTrajectoriesLayer({ const wellsLayer = new AdvancedWellsLayer({ id: id, + name, data: { type: "FeatureCollection", unit: "m", diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts index 483775327..8e8d5ba47 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer.ts @@ -1,10 +1,17 @@ -import { CompositeLayer, GetPickingInfoParams, LayersList, PickingInfo } from "@deck.gl/core"; +import { + CompositeLayer, + GetPickingInfoParams, + LayersList, + type Material, + PickingInfo, + type UpdateParameters, +} from "@deck.gl/core"; import * as vec3 from "@lib/utils/vec3"; import { ExtendedLayerProps, LayerPickInfo } from "@webviz/subsurface-viewer"; import { BoundingBox3D, ReportBoundingBoxAction } from "@webviz/subsurface-viewer/dist/components/Map"; -import { PipeLayer } from "./_private/PipeLayer"; -import { getCoordinateForMd, getMd } from "./_private/wellTrajectoryUtils"; +import { type PipeLayerProps, PipesLayer } from "./_private/PipeLayer"; +import { getMd } from "./_private/wellTrajectoryUtils"; export type WellsLayerData = { coordinates: [number, number, number][]; @@ -22,35 +29,63 @@ export interface WellsLayerProps extends ExtendedLayerProps { reportBoundingBox?: React.Dispatch; } +const MATERIAL: Material = { + ambient: 0.2, + diffuse: 0.6, + shininess: 132, + specularColor: [255, 255, 255], +}; + export class WellsLayer extends CompositeLayer { static layerName: string = "WellsLayer"; // @ts-expect-error - This is how deck.gl expects the state to be defined state!: { hoveredPipeIndex: number | null; - mdCoordinate: [number, number, number] | null; + pipesLayerData: PipeLayerProps["data"]; }; - updateState(params: any): void { + initializeState(): void { + this.setState({ + hoveredPipeIndex: null, + pipesLayerData: [], + }); + } + + shouldUpdateState({ changeFlags }: UpdateParameters): boolean { + return changeFlags.dataChanged !== false; + } + + updateState(params: UpdateParameters): void { super.updateState(params); const { boundingBox } = this.props; + if (params.changeFlags.dataChanged) { + const pipesLayerData = this.props.data.map((well) => { + return { + id: well.properties.uuid, + centerLinePath: well.coordinates.map((coord) => { + return { x: coord[0], y: coord[1], z: coord[2] }; + }), + }; + }); + + this.setState({ pipesLayerData }); + } + this.props.reportBoundingBox?.({ layerBoundingBox: boundingBox, }); } getPickingInfo({ info }: GetPickingInfoParams): LayerPickInfo { - if (!info.sourceLayer?.id.includes("pipe-layer")) { - this.setState({ mdCoordinate: null }); - + if (!info.sourceLayer?.id.includes("pipes-layer")) { return info; } const wellbore = this.props.data[info.index]; if (!wellbore) { - this.setState({ mdCoordinate: null }); return info; } info.object = this.props.data[info.index]; @@ -62,12 +97,6 @@ export class WellsLayer extends CompositeLayer { const md = getMd(vec3.fromArray(coordinate), wellbore.properties.mdArray, trajectory); if (md !== null) { - const mdCoordinate = getCoordinateForMd(md, wellbore.properties.mdArray, trajectory); - if (mdCoordinate === null) { - this.setState({ mdCoordinate: null }); - } else { - this.setState({ mdCoordinate: [mdCoordinate?.x ?? 0, mdCoordinate?.y ?? 0, mdCoordinate?.z ?? 0] }); - } return { ...info, properties: [ @@ -78,7 +107,6 @@ export class WellsLayer extends CompositeLayer { ], }; } - this.setState({ mdCoordinate: null }); return info; } @@ -99,23 +127,12 @@ export class WellsLayer extends CompositeLayer { } renderLayers(): LayersList { + const { pipesLayerData } = this.state; return [ - new PipeLayer({ - id: "pipe-layer", - data: this.props.data.map((well) => { - return { - id: well.properties.uuid, - centerLinePath: well.coordinates.map((coord) => { - return { x: coord[0], y: coord[1], z: coord[2] }; - }), - }; - }), - material: { - ambient: 0.2, - diffuse: 0.6, - shininess: 132, - specularColor: [255, 255, 255], - }, + new PipesLayer({ + id: "pipes-layer", + data: pipesLayerData, + material: MATERIAL, pickable: true, // @ts-expect-error - This is how deck.gl expects the state to be defined parameters: { depthTest: true }, diff --git a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts index f0ef63538..d7ed80a81 100644 --- a/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts +++ b/frontend/src/modules/_shared/customDeckGlLayers/WellsLayer/_private/PipeLayer.ts @@ -35,7 +35,7 @@ export type PipeLayerProps = { material?: Material; } & LayerProps; -export class PipeLayer extends Layer { +export class PipesLayer extends Layer { static layerName: string = "PipeLayer"; // @ts-expect-error - This is how deck.gl expects the state to be defined @@ -87,9 +87,17 @@ export class PipeLayer extends Layer { } } + shouldUpdateState({ changeFlags }: UpdateParameters): boolean { + return changeFlags.dataChanged !== false; + } + updateState(params: UpdateParameters>): void { super.updateState(params); + if (!params.changeFlags.dataChanged) { + return; + } + if (!isEqual(params.props.data, params.oldProps.data)) { this.setState({ models: this.makeModels(params.context), From 9dcb5410f77385cfbfa05c3ca40c9c3e1929a2a1 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 2 May 2025 14:19:15 +0200 Subject: [PATCH 73/97] wip --- .../delegates/SettingsContextDelegate.ts | 142 ++++++------ .../delegates/SharedSettingsDelegate.ts | 124 ++--------- .../delegates/_utils/Dependency.ts | 54 +++-- .../framework/DataProvider/DataProvider.ts | 52 ++--- .../DataProviderManager.ts | 11 - .../framework/DeltaSurface/DeltaSurface.ts | 2 +- .../ExternalSettingController.ts | 129 +++++++++++ .../SettingManager/SettingManager.ts | 205 ++++++++++++------ .../SettingManagerComponent.tsx | 16 +- .../framework/SharedSetting/SharedSetting.ts | 9 +- .../utilityComponents/RemoveItemButton.tsx | 5 +- .../customSettingsHandler.ts | 4 +- .../interfacesAndTypes/entities.ts | 1 + .../visualization/VisualizationAssembler.ts | 4 + 14 files changed, 436 insertions(+), 322 deletions(-) create mode 100644 frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts index 6491c44b6..9209c8323 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts @@ -7,12 +7,9 @@ import { DataProviderManagerTopic, type GlobalSettings, } from "../framework/DataProviderManager/DataProviderManager"; -import { Group } from "../framework/Group/Group"; import type { SettingManager } from "../framework/SettingManager/SettingManager"; import { SettingTopic } from "../framework/SettingManager/SettingManager"; -import { SharedSetting } from "../framework/SharedSetting/SharedSetting"; import type { CustomSettingsHandler, SettingAttributes, UpdateFunc } from "../interfacesAndTypes/customSettingsHandler"; -import { type SharedSettingsProvider, instanceofSharedSettingsProvider } from "../interfacesAndTypes/entities"; import type { SerializedSettingsState } from "../interfacesAndTypes/serialization"; import type { NullableStoredData, StoredData } from "../interfacesAndTypes/sharedTypes"; import type { AvailableValuesType, SettingsKeysFromTuple } from "../interfacesAndTypes/utils"; @@ -28,14 +25,12 @@ export enum SettingsContextStatus { } export enum SettingsContextDelegateTopic { - SETTINGS_CHANGED = "SETTINGS_CHANGED", - STORED_DATA_CHANGED = "STORED_DATA_CHANGED", + SETTINGS_AND_STORED_DATA_CHANGED = "SETTINGS_CHANGED", STATUS = "LOADING_STATE_CHANGED", } export type SettingsContextDelegatePayloads = { - [SettingsContextDelegateTopic.SETTINGS_CHANGED]: void; - [SettingsContextDelegateTopic.STORED_DATA_CHANGED]: void; + [SettingsContextDelegateTopic.SETTINGS_AND_STORED_DATA_CHANGED]: void; [SettingsContextDelegateTopic.STATUS]: SettingsContextStatus; }; @@ -52,7 +47,6 @@ export type SettingsContextDelegateState = {} as NullableStoredData; + private _storedDataLoadingStatus: { [K in TStoredDataKey]: boolean } = {} as { + [K in TStoredDataKey]: boolean; + }; private _dependencies: Dependency[] = []; constructor( @@ -98,6 +95,15 @@ export class SettingsContextDelegate< this._customSettingsHandler = customSettingsHandler; this._dataProviderManager = dataProviderManager; + this._unsubscribeHandler.registerUnsubscribeFunction( + "dependencies", + this.getDataProviderManager() + .getPublishSubscribeDelegate() + .makeSubscriberFunction(DataProviderManagerTopic.GLOBAL_SETTINGS)(() => { + this.handleSettingChanged(); + }), + ); + for (const key in settings) { this._unsubscribeHandler.registerUnsubscribeFunction( "settings", @@ -111,23 +117,6 @@ export class SettingsContextDelegate< this.handleSettingsLoadingStateChanged(); }), ); - this._unsubscribeHandler.registerUnsubscribeFunction( - "data-provider-manager", - dataProviderManager - .getPublishSubscribeDelegate() - .makeSubscriberFunction(DataProviderManagerTopic.SHARED_SETTINGS_CHANGED)(() => { - this.handleSharedSettingsChanged(); - }), - ); - - this._unsubscribeHandler.registerUnsubscribeFunction( - "data-provider-manager", - dataProviderManager - .getPublishSubscribeDelegate() - .makeSubscriberFunction(DataProviderManagerTopic.ITEMS)(() => { - this.handleSharedSettingsChanged(); - }), - ); } this._settings = settings; @@ -158,26 +147,6 @@ export class SettingsContextDelegate< return settings; } - handleSharedSettingsChanged() { - const parentGroup = this._owner.getItemDelegate().getParentGroup(); - if (!parentGroup) { - return; - } - - const sharedSettingsProviders: SharedSettingsProvider[] = parentGroup.getAncestorAndSiblingItems( - (item) => item instanceof SharedSetting, - ) as unknown as SharedSettingsProvider[]; - - const ancestorGroups: SharedSettingsProvider[] = parentGroup.getAncestors( - (item) => item instanceof Group && instanceofSharedSettingsProvider(item), - ) as unknown as SharedSettingsProvider[]; - sharedSettingsProviders.push(...ancestorGroups); - - for (const key in this._settings) { - this._settings[key].checkForOverrides(sharedSettingsProviders); - } - } - areCurrentSettingsValid(): boolean { for (const key in this._settings) { if (!this._settings[key].isValueValid()) { @@ -208,6 +177,16 @@ export class SettingsContextDelegate< return true; } + isAllStoredDataLoaded(): boolean { + for (const key in this._storedDataLoadingStatus) { + if (this._storedDataLoadingStatus[key]) { + return false; + } + } + + return true; + } + areAllSettingsInitialized(): boolean { for (const key in this._settings) { if (!this._settings[key].isInitialized() || this._settings[key].isPersistedValue()) { @@ -247,19 +226,13 @@ export class SettingsContextDelegate< setAvailableValues(key: K, availableValues: AvailableValuesType): void { const settingDelegate = this._settings[key]; settingDelegate.setAvailableValues(availableValues); - - this.getDataProviderManager().publishTopic(DataProviderManagerTopic.AVAILABLE_SETTINGS_CHANGED); } - setStoredData(key: K, data: TStoredData[K] | null): void { + setStoredData(key: K, data: TStoredData[K] | null): void { this._storedData[key] = data; + this._storedDataLoadingStatus[key] = false; - if (!this.areAllDependenciesLoaded()) { - this.setStatus(SettingsContextStatus.LOADING); - return; - } - - this._publishSubscribeDelegate.notifySubscribers(SettingsContextDelegateTopic.STORED_DATA_CHANGED); + this.handleSettingChanged(); } getSettings() { @@ -276,10 +249,7 @@ export class SettingsContextDelegate< makeSnapshotGetter(topic: T): () => SettingsContextDelegatePayloads[T] { const snapshotGetter = (): any => { - if (topic === SettingsContextDelegateTopic.SETTINGS_CHANGED) { - return; - } - if (topic === SettingsContextDelegateTopic.STORED_DATA_CHANGED) { + if (topic === SettingsContextDelegateTopic.SETTINGS_AND_STORED_DATA_CHANGED) { return; } if (topic === SettingsContextDelegateTopic.STATUS) { @@ -323,7 +293,8 @@ export class SettingsContextDelegate< const makeLocalSettingGetter = (key: K, handler: (value: TSettingTypes[K]) => void) => { const handleChange = (): void => { - handler(this._settings[key].getValue() as unknown as TSettingTypes[K]); + const setting = this._settings[key]; + handler(setting.getValue() as unknown as TSettingTypes[K]); }; this._unsubscribeHandler.registerUnsubscribeFunction( "dependencies", @@ -332,6 +303,16 @@ export class SettingsContextDelegate< ), ); + this._unsubscribeHandler.registerUnsubscribeFunction( + "dependencies", + this._settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_LOADING)(() => { + if (!this._settings[key].isLoading()) { + handleChange(); + } + } + ), + ); + this._unsubscribeHandler.registerUnsubscribeFunction( "dependencies", this._settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_PERSISTED)( @@ -359,6 +340,10 @@ export class SettingsContextDelegate< return handleChange; }; + const loadingStateGetter = (settingKey: K): boolean => { + return this._settings[settingKey].isLoading(); + } + const availableSettingsUpdater = ( settingKey: K, updateFunc: UpdateFunc, TSettings, TSettingTypes, TSettingKey>, @@ -367,6 +352,7 @@ export class SettingsContextDelegate< this, updateFunc, makeLocalSettingGetter, + loadingStateGetter, makeGlobalSettingGetter, ); this._dependencies.push(dependency); @@ -377,14 +363,21 @@ export class SettingsContextDelegate< return; } this.setAvailableValues(settingKey, availableValues); + this.handleSettingChanged(); }); - dependency.subscribeLoading((loading: boolean, hasDependencies: boolean) => { + dependency.subscribeLoading((loading: boolean) => { + if (loading) { + this._settings[settingKey].setLoading(loading); + } + this.handleSettingChanged(); + /* this._settings[settingKey].setLoading(loading); if (!hasDependencies && !loading) { this.handleSettingChanged(); } + */ }); dependency.initialize(); @@ -400,6 +393,7 @@ export class SettingsContextDelegate< this, updateFunc, makeLocalSettingGetter, + loadingStateGetter, makeGlobalSettingGetter, ); this._dependencies.push(dependency); @@ -425,13 +419,28 @@ export class SettingsContextDelegate< TSettings, TSettingTypes, TSettingKey - >(this, updateFunc, makeLocalSettingGetter, makeGlobalSettingGetter); + >(this, updateFunc, makeLocalSettingGetter,loadingStateGetter, makeGlobalSettingGetter); this._dependencies.push(dependency); dependency.subscribe((storedData: TStoredData[K] | null) => { this.setStoredData(key, storedData); }); + dependency.subscribeLoading((loading: boolean) => { + if (loading) { + this._storedData[key] = null; + this._storedDataLoadingStatus[key] = loading; + this.handleSettingChanged(); + } + /* + this._settings[settingKey].setLoading(loading); + + if (!hasDependencies && !loading) { + this.handleSettingChanged(); + } + */ + }); + dependency.initialize(); return dependency; @@ -451,10 +460,15 @@ export class SettingsContextDelegate< this, update, makeLocalSettingGetter, + loadingStateGetter, makeGlobalSettingGetter, ); this._dependencies.push(dependency); + dependency.subscribeLoading(() => { + this.handleSettingChanged(); + }); + dependency.initialize(); return dependency; @@ -487,18 +501,18 @@ export class SettingsContextDelegate< } private handleSettingChanged() { - if (!this.areAllSettingsLoaded() || !this.areAllDependenciesLoaded() || !this.areAllSettingsInitialized()) { + if (!this.areAllSettingsLoaded() || !this.areAllDependenciesLoaded() || !this.isAllStoredDataLoaded()) { this.setStatus(SettingsContextStatus.LOADING); return; } - if (this.isSomePersistedSettingNotValid() || !this.areCurrentSettingsValid()) { + if (this.isSomePersistedSettingNotValid() || !this.areCurrentSettingsValid() || !this.areAllSettingsInitialized()) { this.setStatus(SettingsContextStatus.INVALID_SETTINGS); return; } this.setStatus(SettingsContextStatus.VALID_SETTINGS); - this._publishSubscribeDelegate.notifySubscribers(SettingsContextDelegateTopic.SETTINGS_CHANGED); + this._publishSubscribeDelegate.notifySubscribers(SettingsContextDelegateTopic.SETTINGS_AND_STORED_DATA_CHANGED); } private handleSettingsLoadingStateChanged() { diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts index c0e6458f3..c130bed43 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts @@ -1,13 +1,9 @@ -import { DataProvider } from "../framework/DataProvider/DataProvider"; -import { DataProviderManagerTopic } from "../framework/DataProviderManager/DataProviderManager"; -import { Group } from "../framework/Group/Group"; +import { ExternalSettingController } from "../framework/ExternalSettingController/ExternalSettingController"; import type { SettingManager } from "../framework/SettingManager/SettingManager"; -import { SettingTopic } from "../framework/SettingManager/SettingManager"; import type { Item } from "../interfacesAndTypes/entities"; -import type { AvailableValuesType, SettingsKeysFromTuple } from "../interfacesAndTypes/utils"; +import type { SettingsKeysFromTuple } from "../interfacesAndTypes/utils"; import type { SettingTypes, Settings } from "../settings/settingsDefinitions"; -import { settingCategoryAvailableValuesIntersectionReducerMap } from "../settings/settingsDefinitions"; import { UnsubscribeHandlerDelegate } from "./UnsubscribeHandlerDelegate"; @@ -16,126 +12,34 @@ export class SharedSettingsDelegate< TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, > { private _parentItem: Item; - private _wrappedSettings: { [K in TSettingKey]: SettingManager } = {} as { - [K in TSettingKey]: SettingManager; + private _externalSettingControllers: { [K in TSettingKey]: ExternalSettingController } = {} as { + [K in TSettingKey]: ExternalSettingController; + }; + private _wrappedSettings: { [K in TSettingKey]: SettingManager } = {} as { + [K in TSettingKey]: SettingManager; }; private _unsubscribeHandler: UnsubscribeHandlerDelegate = new UnsubscribeHandlerDelegate(); constructor(parentItem: Item, wrappedSettings: { [K in TSettingKey]: SettingManager }) { - this._wrappedSettings = wrappedSettings; this._parentItem = parentItem; + this._wrappedSettings = wrappedSettings; + + for (const key in wrappedSettings) { + const setting = wrappedSettings[key]; + const externalSettingController = new ExternalSettingController(parentItem, setting); + this._externalSettingControllers[key] = externalSettingController; + } const dataProviderManager = parentItem.getItemDelegate().getDataProviderManager(); if (!dataProviderManager) { throw new Error("SharedSettingDelegate must have a parent item with a data provider manager."); } - - for (const key in wrappedSettings) { - this._unsubscribeHandler.registerUnsubscribeFunction( - "setting", - wrappedSettings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.VALUE)(() => { - this.publishValueChange(); - }), - ); - } - - this._unsubscribeHandler.registerUnsubscribeFunction( - "data-provider-manager", - dataProviderManager.getPublishSubscribeDelegate().makeSubscriberFunction(DataProviderManagerTopic.ITEMS)( - () => { - this.makeIntersectionOfAvailableValues(); - }, - ), - ); - this._unsubscribeHandler.registerUnsubscribeFunction( - "data-provider-manager", - dataProviderManager - .getPublishSubscribeDelegate() - .makeSubscriberFunction(DataProviderManagerTopic.SETTINGS_CHANGED)(() => { - this.makeIntersectionOfAvailableValues(); - }), - ); - this._unsubscribeHandler.registerUnsubscribeFunction( - "data-provider-manager", - dataProviderManager - .getPublishSubscribeDelegate() - .makeSubscriberFunction(DataProviderManagerTopic.AVAILABLE_SETTINGS_CHANGED)(() => { - this.makeIntersectionOfAvailableValues(); - }), - ); } getWrappedSettings(): { [K in TSettingKey]: SettingManager } { return this._wrappedSettings; } - publishValueChange(): void { - const dataProviderManager = this._parentItem.getItemDelegate().getDataProviderManager(); - if (dataProviderManager) { - dataProviderManager.publishTopic(DataProviderManagerTopic.SHARED_SETTINGS_CHANGED); - } - } - - private makeIntersectionOfAvailableValues(): void { - let parentGroup = this._parentItem.getItemDelegate().getParentGroup(); - if (this._parentItem instanceof Group) { - parentGroup = this._parentItem.getGroupDelegate(); - } - - if (!parentGroup) { - return; - } - - const providers = parentGroup.getDescendantItems((item) => item instanceof DataProvider) as DataProvider< - any, - any - >[]; - const availableValuesMap: { [K in TSettingKey]: AvailableValuesType } = {} as { - [K in TSettingKey]: AvailableValuesType; - }; - const indices: { [K in TSettingKey]: number } = {} as { [K in TSettings[number]]: number }; - - for (const provider of providers) { - for (const key in this._wrappedSettings) { - const wrappedSetting = this._wrappedSettings[key]; - const category = wrappedSetting.getCategory(); - const index = indices[key] ?? 0; - const setting = provider.getSettingsContextDelegate().getSettings()[wrappedSetting.getType()]; - if (setting) { - if (setting.isLoading()) { - wrappedSetting.setLoading(true); - continue; - } - - if (setting.getAvailableValues() === null) { - continue; - } - - const reducerDefinition = settingCategoryAvailableValuesIntersectionReducerMap[category]; - if (reducerDefinition) { - const { reducer, startingValue } = reducerDefinition; - if (index === 0) { - availableValuesMap[key] = startingValue as AvailableValuesType; - } - availableValuesMap[key] = reducer( - availableValuesMap[key] as any, - setting.getAvailableValues(), - index, - ) as AvailableValuesType; - } - indices[key] = index + 1; - } - } - } - - for (const key in this._wrappedSettings) { - const wrappedSetting = this._wrappedSettings[key]; - wrappedSetting.setLoading(false); - wrappedSetting.setAvailableValues(availableValuesMap[key] ?? []); - this.publishValueChange(); - } - } - unsubscribeAll(): void { this._unsubscribeHandler.unsubscribeAll(); } diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts index 56e520342..092e33f03 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts @@ -1,14 +1,15 @@ import { isCancelledError } from "@tanstack/react-query"; -import { isEqual } from "lodash"; import type { GlobalSettings } from "../../framework/DataProviderManager/DataProviderManager"; import { SettingTopic } from "../../framework/SettingManager/SettingManager"; -import { CancelUpdate } from "../../interfacesAndTypes/customSettingsHandler"; import type { UpdateFunc } from "../../interfacesAndTypes/customSettingsHandler"; import type { SettingsKeysFromTuple } from "../../interfacesAndTypes/utils"; import type { MakeSettingTypesMap, Settings } from "../../settings/settingsDefinitions"; import type { SettingsContextDelegate } from "../SettingsContextDelegate"; +class DependencyLoadingError extends Error { +} + /* * Dependency class is used to represent a node in the dependency graph of a data provider settings context. * It can be compared to an atom in Jotai. @@ -34,6 +35,7 @@ export class Dependency< private _contextDelegate: SettingsContextDelegate; private _makeLocalSettingGetter: (key: K, handler: (value: TSettingTypes[K]) => void) => void; + private _localSettingLoadingStateGetter: (key: K) => boolean; private _makeGlobalSettingGetter: ( key: K, handler: (value: GlobalSettings[K]) => void, @@ -51,6 +53,7 @@ export class Dependency< contextDelegate: SettingsContextDelegate, updateFunc: UpdateFunc, makeLocalSettingGetter: (key: K, handler: (value: TSettingTypes[K]) => void) => void, + localSettingLoadingStateGetter: (key: K) => boolean, makeGlobalSettingGetter: ( key: K, handler: (value: GlobalSettings[K]) => void, @@ -59,6 +62,7 @@ export class Dependency< this._contextDelegate = contextDelegate; this._updateFunc = updateFunc; this._makeLocalSettingGetter = makeLocalSettingGetter; + this._localSettingLoadingStateGetter = localSettingLoadingStateGetter; this._makeGlobalSettingGetter = makeGlobalSettingGetter; this.getGlobalSetting = this.getGlobalSetting.bind(this); @@ -106,6 +110,10 @@ export class Dependency< this._numParentDependencies++; } + if (this._localSettingLoadingStateGetter(settingName)) { + throw new DependencyLoadingError("Setting is loading"); + } + // If the dependency has already subscribed to this setting, return the cached value // that is updated when the setting changes if (this._cachedSettingsMap.has(settingName as string)) { @@ -118,7 +126,7 @@ export class Dependency< this._makeLocalSettingGetter(settingName, (value) => { this._cachedSettingsMap.set(settingName as string, value); - this.callUpdateFunc(); + this.invalidate(); }); setting.getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_LOADING)(() => { @@ -157,12 +165,8 @@ export class Dependency< } this._makeGlobalSettingGetter(settingName, (value) => { - const cachedValue = this._cachedGlobalSettingsMap.get(settingName as string); - if (isEqual(value, cachedValue)) { - return; - } this._cachedGlobalSettingsMap.set(settingName as string, value); - this.callUpdateFunc(); + this.invalidate(); }); this._cachedGlobalSettingsMap.set( @@ -177,6 +181,10 @@ export class Dependency< this._numParentDependencies++; } + if (dep.getIsLoading()) { + throw new DependencyLoadingError("Dependency is loading"); + } + if (this._cachedDependenciesMap.has(dep)) { return this._cachedDependenciesMap.get(dep); } @@ -186,7 +194,7 @@ export class Dependency< dep.subscribe((newValue) => { this._cachedDependenciesMap.set(dep, newValue); - this.callUpdateFunc(); + this.invalidate(); }, true); dep.subscribeLoading((loading) => { @@ -220,6 +228,14 @@ export class Dependency< this._isInitialized = true; } + private invalidate(): void { + if (!this._isLoading) { + this.setLoadingState(true); + } + this.callUpdateFunc(); + } + + private async callUpdateFunc() { if (this._abortController) { this._abortController.abort(); @@ -228,9 +244,7 @@ export class Dependency< this._abortController = new AbortController(); - this.setLoadingState(true); - - let newValue: Awaited | null | typeof CancelUpdate = null; + let newValue: Awaited | null = null; try { newValue = await this._updateFunc({ getLocalSetting: this.getLocalSetting, @@ -239,6 +253,10 @@ export class Dependency< abortSignal: this._abortController.signal, }); } catch (e: any) { + if (e instanceof DependencyLoadingError) { + return; + } + if (!isCancelledError(e)) { this.applyNewValue(null); return; @@ -246,20 +264,14 @@ export class Dependency< return; } - if (newValue === CancelUpdate) { - return; - } - this.applyNewValue(newValue); } private applyNewValue(newValue: Awaited | null) { this.setLoadingState(false); - if (!isEqual(newValue, this._cachedValue) || newValue === null) { - this._cachedValue = newValue; - for (const callback of this._dependencies) { - callback(newValue); - } + this._cachedValue = newValue; + for (const callback of this._dependencies) { + callback(newValue); } } } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts index a7b1d51de..c6d70e24e 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts @@ -120,6 +120,7 @@ export class DataProvider< private _isSubordinated: boolean = false; private _prevSettings: TSettingTypes | null = null; private _prevStoredData: NullableStoredData | null = null; + private _currentTransactionId: number = 0; constructor(params: DataProviderParams) { const { @@ -150,16 +151,7 @@ export class DataProvider< "settings-context", this._settingsContextDelegate .getPublishSubscribeDelegate() - .makeSubscriberFunction(SettingsContextDelegateTopic.SETTINGS_CHANGED)(() => { - this.handleSettingsAndStoredDataChange(); - }), - ); - - this._unsubscribeHandler.registerUnsubscribeFunction( - "settings-context", - this._settingsContextDelegate - .getPublishSubscribeDelegate() - .makeSubscriberFunction(SettingsContextDelegateTopic.STORED_DATA_CHANGED)(() => { + .makeSubscriberFunction(SettingsContextDelegateTopic.SETTINGS_AND_STORED_DATA_CHANGED)(() => { this.handleSettingsAndStoredDataChange(); }), ); @@ -172,15 +164,6 @@ export class DataProvider< this.handleSettingsStatusChange(); }), ); - - this._unsubscribeHandler.registerUnsubscribeFunction( - "data-provider-manager", - dataProviderManager - .getPublishSubscribeDelegate() - .makeSubscriberFunction(DataProviderManagerTopic.GLOBAL_SETTINGS)(() => { - this.handleSettingsAndStoredDataChange(); - }), - ); } areCurrentSettingsValid(): boolean { @@ -192,6 +175,11 @@ export class DataProvider< } handleSettingsAndStoredDataChange(): void { + if (this._settingsContextDelegate.getStatus() === SettingsContextStatus.LOADING) { + this.setStatus(DataProviderStatus.LOADING); + return; + } + if (!this.areCurrentSettingsValid()) { this._error = "Invalid settings"; this.setStatus(DataProviderStatus.INVALID_SETTINGS); @@ -223,17 +211,21 @@ export class DataProvider< } if (!refetchRequired) { - this._publishSubscribeDelegate.notifySubscribers(DataProviderTopic.DATA); - this._dataProviderManager.publishTopic(DataProviderManagerTopic.DATA_REVISION); this.setStatus(DataProviderStatus.SUCCESS); return; } this._cancellationPending = true; - this._prevSettings = clone(this._settingsContextDelegate.getValues()) as TSettingTypes; - this._prevStoredData = clone(this._settingsContextDelegate.getStoredDataRecord()) as TStoredData; + + // It might be that we started a new transaction while the previous one was still running. + // In this case, we need to make sure that we only use the latest transaction and cancel the previous one. + this._currentTransactionId += 1; + this.maybeCancelQuery().then(() => { - this.maybeRefetchData(); + this.maybeRefetchData().then(() => { + this._prevSettings = clone(this._settingsContextDelegate.getValues()) as TSettingTypes; + this._prevStoredData = clone(this._settingsContextDelegate.getStoredDataRecord()) as TStoredData; + }); }); } @@ -345,6 +337,8 @@ export class DataProvider< } async maybeRefetchData(): Promise { + const thisTransactionId = this._currentTransactionId; + const queryClient = this.getQueryClient(); if (!queryClient) { @@ -371,6 +365,15 @@ export class DataProvider< queryClient, registerQueryKey: (key) => this.registerQueryKey(key), }); + + // This is a security check to make sure that we are not using a stale transaction id. + // This can happen if the transaction id is incremented while the async fetch data function is still running. + // Queries are cancelled in the maybeCancelQuery function and should, hence, throw a cancelled error. + // However, there might me some operations following after the query execution that are not cancelled. + if (this._currentTransactionId !== thisTransactionId) { + return; + } + if (this._customDataProviderImpl.makeValueRange) { this._valueRange = this._customDataProviderImpl.makeValueRange(accessors); } @@ -381,7 +384,6 @@ export class DataProvider< } this._queryKeys = []; this._publishSubscribeDelegate.notifySubscribers(DataProviderTopic.DATA); - this._dataProviderManager.publishTopic(DataProviderManagerTopic.DATA_REVISION); this.setStatus(DataProviderStatus.SUCCESS); } catch (error: any) { if (isCancelledError(error)) { diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts index 60e46f7f8..569401659 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts @@ -24,19 +24,15 @@ import { type SerializedDataProviderManager, SerializedType } from "../../interf export enum DataProviderManagerTopic { ITEMS = "ITEMS", SETTINGS_CHANGED = "SETTINGS_CHANGED", - AVAILABLE_SETTINGS_CHANGED = "AVAILABLE_SETTINGS_CHANGED", DATA_REVISION = "DATA_REVISION", GLOBAL_SETTINGS = "GLOBAL_SETTINGS", - SHARED_SETTINGS_CHANGED = "SHARED_SETTINGS_CHANGED", } export type DataProviderManagerTopicPayload = { [DataProviderManagerTopic.ITEMS]: Item[]; [DataProviderManagerTopic.SETTINGS_CHANGED]: void; - [DataProviderManagerTopic.AVAILABLE_SETTINGS_CHANGED]: void; [DataProviderManagerTopic.DATA_REVISION]: number; [DataProviderManagerTopic.GLOBAL_SETTINGS]: GlobalSettings; - [DataProviderManagerTopic.SHARED_SETTINGS_CHANGED]: void; }; export type GlobalSettings = { @@ -164,18 +160,12 @@ export class DataProviderManager implements ItemGroup, PublishSubscribe { + .makeSubscriberFunction(SettingsContextDelegateTopic.SETTINGS_AND_STORED_DATA_CHANGED)(() => { this.handleSettingsChange(); }), ); diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts new file mode 100644 index 000000000..915429d78 --- /dev/null +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts @@ -0,0 +1,129 @@ +import { UnsubscribeHandlerDelegate } from "../../delegates/UnsubscribeHandlerDelegate"; +import type { Item } from "../../interfacesAndTypes/entities"; +import type { AvailableValuesType, MakeAvailableValuesTypeBasedOnCategory } from "../../interfacesAndTypes/utils"; +import { + type Setting, + type SettingCategories, + type SettingTypes, + settingCategoryAvailableValuesIntersectionReducerMap, +} from "../../settings/settingsDefinitions"; +import { DataProvider } from "../DataProvider/DataProvider"; +import { DataProviderManagerTopic } from "../DataProviderManager/DataProviderManager"; +import { Group } from "../Group/Group"; +import type { SettingManager } from "../SettingManager/SettingManager"; + +export class ExternalSettingController< + TSetting extends Setting, + TValue extends SettingTypes[TSetting] = SettingTypes[TSetting], + TCategory extends SettingCategories[TSetting] = SettingCategories[TSetting], +> { + private _parentItem: Item; + private _setting: SettingManager; + private _controlledSettings: Map> = new Map(); + private _availableValuesMap: Map> = new Map(); + private _unsubscribeHandler: UnsubscribeHandlerDelegate = new UnsubscribeHandlerDelegate(); + + constructor(parentItem: Item, setting: SettingManager) { + this._parentItem = parentItem; + this._setting = setting; + + const dataProviderManager = parentItem.getItemDelegate().getDataProviderManager(); + this._unsubscribeHandler.registerUnsubscribeFunction( + "data-provider-manager", + dataProviderManager.getPublishSubscribeDelegate().makeSubscriberFunction(DataProviderManagerTopic.ITEMS)( + () => { + this.updateControlledSettings(); + }, + ), + ); + } + + getParentItem(): Item { + return this._parentItem; + } + + registerSetting(settingManager: SettingManager): void { + this._controlledSettings.set(settingManager.getId(), settingManager); + settingManager.registerExternalSettingController(this); + } + + getSetting(): SettingManager { + return this._setting; + } + + private updateControlledSettings(): void { + this.unregisterAllControlledSettings(); + + let parentGroup = this._parentItem.getItemDelegate().getParentGroup(); + if (this._parentItem instanceof Group) { + parentGroup = this._parentItem.getGroupDelegate(); + } + + if (!parentGroup) { + return; + } + + const providers = parentGroup.getDescendantItems((item) => item instanceof DataProvider) as DataProvider< + any, + any + >[]; + + for (const provider of providers) { + const setting = provider.getSettingsContextDelegate().getSettings()[this._setting.getType()]; + if (setting) { + this._controlledSettings.set(setting.getId(), setting); + this._availableValuesMap.set(setting.getId(), setting.getAvailableValues()); + setting.registerExternalSettingController(this); + } + } + + this.makeIntersectionOfAvailableValues(); + } + + unregisterAllControlledSettings(): void { + for (const setting of this._controlledSettings.values()) { + setting.unregisterExternalSettingController(); + } + this._controlledSettings.clear(); + this._availableValuesMap.clear(); + } + + setAvailableValues(availableValues: AvailableValuesType | null): void { + if (availableValues) { + this._availableValuesMap.set(this._setting.getId(), availableValues); + } else { + this._availableValuesMap.delete(this._setting.getId()); + } + + this.makeIntersectionOfAvailableValues(); + } + + makeIntersectionOfAvailableValues(): void { + const category = this._setting.getCategory(); + const reducerDefinition = settingCategoryAvailableValuesIntersectionReducerMap[category]; + + if (!reducerDefinition) { + throw new Error( + `No reducer definition found for category ${category}. Please check the settings definitions.`, + ); + } + + const { reducer, startingValue } = reducerDefinition; + let availableValues: MakeAvailableValuesTypeBasedOnCategory = + startingValue as MakeAvailableValuesTypeBasedOnCategory; + let index = 0; + + for (const value of this._availableValuesMap.values()) { + if (value === null) { + continue; + } + availableValues = reducer( + availableValues as any, + value as any, + index++, + ) as MakeAvailableValuesTypeBasedOnCategory; + } + + this._setting.setAvailableValues(availableValues); + } +} diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts index c3fbbdd3a..4779c33e6 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts @@ -7,12 +7,13 @@ import type { PublishSubscribe } from "@modules/_shared/utils/PublishSubscribeDe import { PublishSubscribeDelegate } from "@modules/_shared/utils/PublishSubscribeDelegate"; +import { UnsubscribeHandlerDelegate } from "../../delegates/UnsubscribeHandlerDelegate"; import type { CustomSettingImplementation } from "../../interfacesAndTypes/customSettingImplementation"; import type { SettingAttributes } from "../../interfacesAndTypes/customSettingsHandler"; -import type { SharedSettingsProvider } from "../../interfacesAndTypes/entities"; import type { AvailableValuesType, MakeAvailableValuesTypeBasedOnCategory } from "../../interfacesAndTypes/utils"; import type { Setting, SettingCategories, SettingCategory, SettingTypes } from "../../settings/settingsDefinitions"; import { settingCategoryFixupMap, settingCategoryIsValueValidMap } from "../../settings/settingsDefinitions"; +import type { ExternalSettingController } from "../ExternalSettingController/ExternalSettingController"; import { Group } from "../Group/Group"; export enum SettingTopic { @@ -20,8 +21,8 @@ export enum SettingTopic { VALUE_ABOUT_TO_BE_CHANGED = "VALUE_ABOUT_TO_BE_CHANGED", IS_VALID = "IS_VALID", AVAILABLE_VALUES = "AVAILABLE_VALUES", - OVERRIDDEN_VALUE = "OVERRIDDEN_VALUE", - OVERRIDDEN_VALUE_PROVIDER = "OVERRIDDEN_VALUE_PROVIDER", + IS_EXTERNALLY_CONTROLLED = "IS_EXTERNALLY_CONTROLLED", + EXTERNAL_CONTROLLER_PROVIDER = "EXTERNAL_CONTROLLER_PROVIDER", IS_LOADING = "IS_LOADING", IS_INITIALIZED = "IS_INITIALIZED", IS_PERSISTED = "IS_PERSISTED", @@ -33,8 +34,8 @@ export type SettingTopicPayloads = { [SettingTopic.VALUE_ABOUT_TO_BE_CHANGED]: void; [SettingTopic.IS_VALID]: boolean; [SettingTopic.AVAILABLE_VALUES]: MakeAvailableValuesTypeBasedOnCategory | null; - [SettingTopic.OVERRIDDEN_VALUE]: TValue | undefined; - [SettingTopic.OVERRIDDEN_VALUE_PROVIDER]: OverriddenValueProviderType | undefined; + [SettingTopic.IS_EXTERNALLY_CONTROLLED]: boolean; + [SettingTopic.EXTERNAL_CONTROLLER_PROVIDER]: OverriddenValueProviderType | undefined; [SettingTopic.IS_LOADING]: boolean; [SettingTopic.IS_INITIALIZED]: boolean; [SettingTopic.IS_PERSISTED]: boolean; @@ -79,8 +80,6 @@ export class SettingManager< private _isValueValid: boolean = false; private _publishSubscribeDelegate = new PublishSubscribeDelegate>(); private _availableValues: MakeAvailableValuesTypeBasedOnCategory | null = null; - private _overriddenValue: TValue | undefined = undefined; - private _overriddenValueProviderType: OverriddenValueProviderType | undefined = undefined; private _loading: boolean = false; private _initialized: boolean = false; private _currentValueFromPersistence: TValue | null = null; @@ -89,6 +88,8 @@ export class SettingManager< enabled: true, visible: true, }; + private _externalController: ExternalSettingController | null = null; + private _unsubscribeHandler: UnsubscribeHandlerDelegate = new UnsubscribeHandlerDelegate(); constructor({ type, @@ -109,6 +110,82 @@ export class SettingManager< } } + registerExternalSettingController( + externalController: ExternalSettingController, + ): void { + this._externalController = externalController; + this._unsubscribeHandler.registerUnsubscribeFunction( + "external-setting-controller", + externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.VALUE)( + () => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.VALUE); + this._value = externalController.getSetting().getValue(); + }, + ), + ); + this._unsubscribeHandler.registerUnsubscribeFunction( + "external-setting-controller", + externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_VALID)( + () => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_VALID); + }, + ), + ); + this._unsubscribeHandler.registerUnsubscribeFunction( + "external-setting-controller", + externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_LOADING)( + () => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_LOADING); + } + ), + ); + this._unsubscribeHandler.registerUnsubscribeFunction( + "external-setting-controller", + externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.ATTRIBUTES)( + () => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.ATTRIBUTES); + } + ), + ); + this._unsubscribeHandler.registerUnsubscribeFunction( + "external-setting-controller", + externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.VALUE_ABOUT_TO_BE_CHANGED)( + () => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.VALUE_ABOUT_TO_BE_CHANGED); + } + ), + ); + this._unsubscribeHandler.registerUnsubscribeFunction( + "external-setting-controller", + externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_INITIALIZED)( + () => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_INITIALIZED); + } + ), + ); + this._unsubscribeHandler.registerUnsubscribeFunction( + "external-setting-controller", + externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_PERSISTED)( + () => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_PERSISTED); + } + ), + ); + this._unsubscribeHandler.registerUnsubscribeFunction( + "external-setting-controller", + externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.AVAILABLE_VALUES)( + () => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.AVAILABLE_VALUES); + } + ), + ); + } + + unregisterExternalSettingController(): void { + this._externalController = null; + this._unsubscribeHandler.unsubscribe("external-setting-controller"); + } + getId(): string { return this._id; } @@ -140,8 +217,8 @@ export class SettingManager< } getValue(): TValue { - if (this._overriddenValue !== undefined) { - return this._overriddenValue; + if (this._externalController) { + return this._externalController.getSetting().getValue(); } if (this._currentValueFromPersistence !== null) { @@ -173,10 +250,16 @@ export class SettingManager< } isValueValid(): boolean { + if (this._externalController) { + return this._externalController.getSetting().isValueValid(); + } return this._isValueValid; } isPersistedValue(): boolean { + if (this._externalController) { + return this._externalController.getSetting().isPersistedValue(); + } return this._currentValueFromPersistence !== null; } @@ -210,6 +293,13 @@ export class SettingManager< if (this._loading === loading) { return; } + + if (this._externalController) { + this._externalController.getSetting().setLoading(loading); + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_LOADING); + return; + } + this._loading = loading; this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_LOADING); } @@ -223,10 +313,16 @@ export class SettingManager< } isInitialized(): boolean { + if (this._externalController) { + return this._externalController.getSetting().isInitialized(); + } return this._initialized || this._isStatic; } isLoading(): boolean { + if (this._externalController) { + return this._externalController.getSetting().isLoading(); + } return this._loading; } @@ -235,6 +331,10 @@ export class SettingManager< workbenchSession: WorkbenchSession, workbenchSettings: WorkbenchSettings, ): React.ReactNode { + if (this._externalController) { + return this._externalController.getSetting().valueToRepresentation(value, workbenchSession, workbenchSettings); + } + if (this._customSettingImplementation.overriddenValueRepresentation) { return this._customSettingImplementation.overriddenValueRepresentation({ value, @@ -258,63 +358,22 @@ export class SettingManager< return "Value has no string representation"; } - checkForOverrides(sharedSettingsProviders: SharedSettingsProvider[]) { - let overriddenValue: TValue | undefined; - let overriddenValueProviderType: OverriddenValueProviderType | undefined; - - for (const provider of sharedSettingsProviders) { - if (!provider.getSharedSettingsDelegate()) { - continue; - } - for (const sharedSettingKey in provider.getSharedSettingsDelegate().getWrappedSettings()) { - const sharedSetting = provider.getSharedSettingsDelegate().getWrappedSettings()[sharedSettingKey]; - if (sharedSetting.getType() === this._type) { - overriddenValue = sharedSetting.getValue(); - overriddenValueProviderType = OverriddenValueProviderType.SHARED_SETTING; - if (provider instanceof Group) { - overriddenValueProviderType = OverriddenValueProviderType.GROUP; - } - break; + makeSnapshotGetter(topic: T): () => SettingTopicPayloads[T] { + const externalController = this._externalController; + if (externalController) { + return (): any => { + if (topic === SettingTopic.IS_EXTERNALLY_CONTROLLED) { + return true; } + if (topic === SettingTopic.EXTERNAL_CONTROLLER_PROVIDER) { + return externalController.getParentItem() instanceof Group + ? OverriddenValueProviderType.GROUP + : OverriddenValueProviderType.SHARED_SETTING; + } + return externalController.getSetting().makeSnapshotGetter(topic)(); } } - this.setOverriddenValue(overriddenValue); - this._overriddenValueProviderType = overriddenValueProviderType; - this._publishSubscribeDelegate.notifySubscribers(SettingTopic.OVERRIDDEN_VALUE_PROVIDER); - } - - setOverriddenValue(overriddenValue: TValue | undefined): void { - if (isEqual(this._overriddenValue, overriddenValue)) { - return; - } - - const prevValue = this._overriddenValue; - this._overriddenValue = overriddenValue; - this._publishSubscribeDelegate.notifySubscribers(SettingTopic.OVERRIDDEN_VALUE); - - if (overriddenValue === undefined) { - // Keep overridden value, if invalid fix it - if (prevValue !== undefined) { - this._value = prevValue; - } - this.maybeFixupValue(); - } - - this.setValueValid(this.checkIfValueIsValid(this.getValue())); - - if (prevValue === undefined && overriddenValue !== undefined && isEqual(this._value, overriddenValue)) { - return; - } - - if (prevValue !== undefined && overriddenValue === undefined && isEqual(this._value, prevValue)) { - return; - } - - this._publishSubscribeDelegate.notifySubscribers(SettingTopic.VALUE); - } - - makeSnapshotGetter(topic: T): () => SettingTopicPayloads[T] { const snapshotGetter = (): any => { switch (topic) { case SettingTopic.VALUE: @@ -325,10 +384,12 @@ export class SettingManager< return this._isValueValid; case SettingTopic.AVAILABLE_VALUES: return this._availableValues; - case SettingTopic.OVERRIDDEN_VALUE: - return this._overriddenValue; - case SettingTopic.OVERRIDDEN_VALUE_PROVIDER: - return this._overriddenValueProviderType; + case SettingTopic.IS_EXTERNALLY_CONTROLLED: + return this._externalController !== null; + case SettingTopic.EXTERNAL_CONTROLLER_PROVIDER: + return this._externalController?.getParentItem() instanceof Group + ? OverriddenValueProviderType.GROUP + : OverriddenValueProviderType.SHARED_SETTING; case SettingTopic.IS_LOADING: return this.isLoading(); case SettingTopic.IS_PERSISTED: @@ -394,18 +455,28 @@ export class SettingManager< } setAvailableValues(availableValues: MakeAvailableValuesTypeBasedOnCategory): void { + if (this._externalController) { + this.initialize(); + this._externalController.setAvailableValues(availableValues); + } + if (isEqual(this._availableValues, availableValues) && this._initialized) { + this.setLoading(false); return; } this._availableValues = availableValues; + let valueChanged = false; - if ((!this.checkIfValueIsValid(this.getValue()) && this.maybeFixupValue()) || this.maybeResetPersistedValue()) { + const valueFixedUp = !this.checkIfValueIsValid(this.getValue()) && this.maybeFixupValue(); + const persistedValueReset = this.maybeResetPersistedValue(); + if (valueFixedUp || persistedValueReset) { valueChanged = true; } const prevIsValid = this._isValueValid; this.setValueValid(this.checkIfValueIsValid(this.getValue())); this.initialize(); + this.setLoading(false); if (valueChanged || this._isValueValid !== prevIsValid) { this._publishSubscribeDelegate.notifySubscribers(SettingTopic.VALUE); } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx index d54c14179..e8bb3cf48 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx @@ -37,10 +37,10 @@ export function SettingComponent< const isValid = usePublishSubscribeTopicValue(props.setting, SettingTopic.IS_VALID); const isPersisted = usePublishSubscribeTopicValue(props.setting, SettingTopic.IS_PERSISTED); const availableValues = usePublishSubscribeTopicValue(props.setting, SettingTopic.AVAILABLE_VALUES); - const overriddenValue = usePublishSubscribeTopicValue(props.setting, SettingTopic.OVERRIDDEN_VALUE); - const overriddenValueProvider = usePublishSubscribeTopicValue( + const isExternallyControlled = usePublishSubscribeTopicValue(props.setting, SettingTopic.IS_EXTERNALLY_CONTROLLED); + const externalControllerProvider = usePublishSubscribeTopicValue( props.setting, - SettingTopic.OVERRIDDEN_VALUE_PROVIDER, + SettingTopic.EXTERNAL_CONTROLLER_PROVIDER, ); const isLoading = usePublishSubscribeTopicValue(props.setting, SettingTopic.IS_LOADING); const isInitialized = usePublishSubscribeTopicValue(props.setting, SettingTopic.IS_INITIALIZED); @@ -68,12 +68,12 @@ export function SettingComponent< ); } - if (overriddenValue !== undefined) { - if (overriddenValueProvider !== OverriddenValueProviderType.SHARED_SETTING) { + if (isExternallyControlled) { + if (externalControllerProvider !== OverriddenValueProviderType.SHARED_SETTING) { return null; } const valueAsString = props.setting.valueToRepresentation( - overriddenValue, + value, props.manager.getWorkbenchSession(), props.manager.getWorkbenchSettings(), ); @@ -109,8 +109,8 @@ export function SettingComponent< onValueChange={handleValueChanged} value={value} isValueValid={isValid} - isOverridden={overriddenValue !== undefined} - overriddenValue={overriddenValue ?? null} + isOverridden={isExternallyControlled} + overriddenValue={value} availableValues={availableValues} globalSettings={globalSettings} workbenchSession={props.manager.getWorkbenchSession()} diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SharedSetting/SharedSetting.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/SharedSetting/SharedSetting.ts index fde3fc23a..3104bc7f6 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SharedSetting/SharedSetting.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SharedSetting/SharedSetting.ts @@ -7,7 +7,7 @@ import type { SerializedSharedSetting } from "../../interfacesAndTypes/serializa import { SerializedType } from "../../interfacesAndTypes/serialization"; import { SettingRegistry } from "../../settings/SettingRegistry"; import type { Setting, SettingTypes } from "../../settings/settingsDefinitions"; -import { type DataProviderManager, DataProviderManagerTopic } from "../DataProviderManager/DataProviderManager"; +import { type DataProviderManager } from "../DataProviderManager/DataProviderManager"; import type { SettingManager } from "../SettingManager/SettingManager"; export function isSharedSetting(obj: any): obj is SharedSetting { @@ -51,13 +51,6 @@ export class SharedSetting implements Item, SharedSett return this._sharedSettingsDelegate; } - publishValueChange(): void { - const dataProviderManager = this._itemDelegate.getDataProviderManager(); - if (dataProviderManager) { - dataProviderManager.publishTopic(DataProviderManagerTopic.SHARED_SETTINGS_CHANGED); - } - } - getWrappedSetting(): SettingManager { return Object.values(this._sharedSettingsDelegate.getWrappedSettings())[0] as SettingManager; } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/utilityComponents/RemoveItemButton.tsx b/frontend/src/modules/_shared/DataProviderFramework/framework/utilityComponents/RemoveItemButton.tsx index 9909519d0..fe8c27bae 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/utilityComponents/RemoveItemButton.tsx +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/utilityComponents/RemoveItemButton.tsx @@ -4,7 +4,6 @@ import { DenseIconButton } from "@lib/components/DenseIconButton"; import { DenseIconButtonColorScheme } from "@lib/components/DenseIconButton/denseIconButton"; import type { Item } from "../../interfacesAndTypes/entities"; -import { DataProvider } from "../DataProvider/DataProvider"; export type RemoveItemButtonProps = { item: Item; @@ -17,9 +16,7 @@ export function RemoveItemButton(props: RemoveItemButtonProps): React.ReactNode parentGroup.removeChild(props.item); } - if (props.item instanceof DataProvider) { - props.item.beforeDestroy(); - } + props.item.beforeDestroy?.(); } return ( diff --git a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts index 54ca7f99c..55d062330 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts @@ -24,8 +24,6 @@ export type SettingAttributes = { enabled: boolean; }; -export const CancelUpdate = Symbol("CancelUpdate"); - export interface UpdateFunc< TReturnValue, TSettings extends Settings, @@ -37,7 +35,7 @@ export interface UpdateFunc< getGlobalSetting: (settingName: T) => GlobalSettings[T]; getHelperDependency: GetHelperDependency; abortSignal: AbortSignal; - }): TReturnValue | typeof CancelUpdate; + }): TReturnValue; } export interface DefineDependenciesArgs< diff --git a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/entities.ts b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/entities.ts index 7e17e7bd1..3509b5083 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/entities.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/entities.ts @@ -14,6 +14,7 @@ export interface Item { getItemDelegate(): ItemDelegate; serializeState(): SerializedItem; deserializeState(serialized: SerializedItem): void; + beforeDestroy?(): void; } export function instanceofItem(item: any): item is Item { diff --git a/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts b/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts index 861854382..67595fb36 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts @@ -321,6 +321,10 @@ export class VisualizationAssembler< numLoadingDataProviders++; } + if (child.getStatus() === DataProviderStatus.INVALID_SETTINGS) { + continue; + } + if (child.getStatus() === DataProviderStatus.ERROR) { const error = child.getError(); if (error) { From a685de970926d553b5d07288038cd955c6e695d0 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 2 May 2025 14:20:44 +0200 Subject: [PATCH 74/97] fix: api --- .../api/autogen/@tanstack/react-query.gen.ts | 499 +++--- frontend/src/api/autogen/index.ts | 2 - frontend/src/api/autogen/sdk.gen.ts | 719 ++++---- frontend/src/api/autogen/types.gen.ts | 1498 ++++++++--------- 4 files changed, 1356 insertions(+), 1362 deletions(-) diff --git a/frontend/src/api/autogen/@tanstack/react-query.gen.ts b/frontend/src/api/autogen/@tanstack/react-query.gen.ts index 2f0de657e..24ba7b4ab 100644 --- a/frontend/src/api/autogen/@tanstack/react-query.gen.ts +++ b/frontend/src/api/autogen/@tanstack/react-query.gen.ts @@ -2,8 +2,90 @@ import type { Options } from "@hey-api/client-axios"; import { queryOptions, type UseMutationOptions, type DefaultError } from "@tanstack/react-query"; -import type { AxiosError } from "axios"; - +import type { + GetFieldsData, + GetCasesData, + GetEnsemblesData, + GetEnsembleDetailsData, + GetVectorListData, + GetDeltaEnsembleVectorListData, + GetRealizationsVectorDataData, + GetDeltaEnsembleRealizationsVectorDataData, + GetTimestampsListData, + GetHistoricalVectorDataData, + GetStatisticalVectorDataData, + GetDeltaEnsembleStatisticalVectorDataData, + GetStatisticalVectorDataPerSensitivityData, + GetRealizationVectorAtTimestampData, + GetTableDefinitionsData, + PostGetAggregatedPerRealizationTableDataData, + PostGetAggregatedPerRealizationTableDataError, + PostGetAggregatedPerRealizationTableDataResponse, + PostGetAggregatedStatisticalTableDataData, + PostGetAggregatedStatisticalTableDataError, + PostGetAggregatedStatisticalTableDataResponse, + GetRealizationSurfacesMetadataData, + GetObservedSurfacesMetadataData, + GetSurfaceDataData, + PostGetSurfaceIntersectionData, + PostGetSurfaceIntersectionError, + PostGetSurfaceIntersectionResponse, + PostGetSampleSurfaceInPointsData, + PostGetSampleSurfaceInPointsError, + PostGetSampleSurfaceInPointsResponse, + GetDeltaSurfaceDataData, + GetMisfitSurfaceDataData, + GetWellboreStratigraphicColumnsData, + GetStratigraphicUnitsData, + GetParameterNamesAndDescriptionData, + GetParameterData, + GetParametersData, + GetIsSensitivityRunData, + GetSensitivitiesData, + GetGridModelsInfoData, + GetGridSurfaceData, + GetGridParameterData, + PostGetPolylineIntersectionData, + PostGetPolylineIntersectionError, + PostGetPolylineIntersectionResponse, + GetRealizationFlowNetworkData, + GetTableDataData, + GetWellCompletionsDataData, + GetDrilledWellboreHeadersData, + GetWellTrajectoriesData, + GetWellborePickIdentifiersData, + GetWellborePicksForPickIdentifierData, + GetWellborePicksForWellboreData, + GetWellborePicksInStratColumnData, + GetWellboreCompletionsData, + GetWellboreCasingsData, + GetWellborePerforationsData, + GetWellboreLogCurveHeadersData, + GetLogCurveDataData, + GetSeismicCubeMetaListData, + GetInlineSliceData, + GetCrosslineSliceData, + GetDepthSliceData, + PostGetSeismicFenceData, + PostGetSeismicFenceError, + PostGetSeismicFenceResponse, + GetPolygonsDirectoryData, + GetPolygonsDataData, + GetUserPhotoData, + GetObservationsData, + GetTableDefinitionData, + GetRealizationDataData, + GetVfpTableNamesData, + GetVfpTableData, + LoginRouteData, + AuthorizedCallbackRouteData, + GetAliveData, + GetAliveProtectedData, + PostLogoutData, + PostLogoutResponse, + GetLoggedInUserData, + RootData, +} from "../types.gen"; import { getFields, getCases, @@ -76,90 +158,7 @@ import { root, client, } from "../sdk.gen"; -import type { - GetFieldsData_api, - GetCasesData_api, - GetEnsemblesData_api, - GetEnsembleDetailsData_api, - GetVectorListData_api, - GetDeltaEnsembleVectorListData_api, - GetRealizationsVectorDataData_api, - GetDeltaEnsembleRealizationsVectorDataData_api, - GetTimestampsListData_api, - GetHistoricalVectorDataData_api, - GetStatisticalVectorDataData_api, - GetDeltaEnsembleStatisticalVectorDataData_api, - GetStatisticalVectorDataPerSensitivityData_api, - GetRealizationVectorAtTimestampData_api, - GetTableDefinitionsData_api, - PostGetAggregatedPerRealizationTableDataData_api, - PostGetAggregatedPerRealizationTableDataError_api, - PostGetAggregatedPerRealizationTableDataResponse_api, - PostGetAggregatedStatisticalTableDataData_api, - PostGetAggregatedStatisticalTableDataError_api, - PostGetAggregatedStatisticalTableDataResponse_api, - GetRealizationSurfacesMetadataData_api, - GetObservedSurfacesMetadataData_api, - GetSurfaceDataData_api, - PostGetSurfaceIntersectionData_api, - PostGetSurfaceIntersectionError_api, - PostGetSurfaceIntersectionResponse_api, - PostGetSampleSurfaceInPointsData_api, - PostGetSampleSurfaceInPointsError_api, - PostGetSampleSurfaceInPointsResponse_api, - GetDeltaSurfaceDataData_api, - GetMisfitSurfaceDataData_api, - GetWellboreStratigraphicColumnsData_api, - GetStratigraphicUnitsData_api, - GetParameterNamesAndDescriptionData_api, - GetParameterData_api, - GetParametersData_api, - GetIsSensitivityRunData_api, - GetSensitivitiesData_api, - GetGridModelsInfoData_api, - GetGridSurfaceData_api, - GetGridParameterData_api, - PostGetPolylineIntersectionData_api, - PostGetPolylineIntersectionError_api, - PostGetPolylineIntersectionResponse_api, - GetRealizationFlowNetworkData_api, - GetTableDataData_api, - GetWellCompletionsDataData_api, - GetDrilledWellboreHeadersData_api, - GetWellTrajectoriesData_api, - GetWellborePickIdentifiersData_api, - GetWellborePicksForPickIdentifierData_api, - GetWellborePicksForWellboreData_api, - GetWellborePicksInStratColumnData_api, - GetWellboreCompletionsData_api, - GetWellboreCasingsData_api, - GetWellborePerforationsData_api, - GetWellboreLogCurveHeadersData_api, - GetLogCurveDataData_api, - GetSeismicCubeMetaListData_api, - GetInlineSliceData_api, - GetCrosslineSliceData_api, - GetDepthSliceData_api, - PostGetSeismicFenceData_api, - PostGetSeismicFenceError_api, - PostGetSeismicFenceResponse_api, - GetPolygonsDirectoryData_api, - GetPolygonsDataData_api, - GetUserPhotoData_api, - GetObservationsData_api, - GetTableDefinitionData_api, - GetRealizationDataData_api, - GetVfpTableNamesData_api, - GetVfpTableData_api, - LoginRouteData_api, - AuthorizedCallbackRouteData_api, - GetAliveData_api, - GetAliveProtectedData_api, - PostLogoutData_api, - PostLogoutResponse_api, - GetLoggedInUserData_api, - RootData_api, -} from "../types.gen"; +import type { AxiosError } from "axios"; type QueryKey = [ Pick & { @@ -195,9 +194,9 @@ const createQueryKey = ( return params; }; -export const getFieldsQueryKey = (options?: Options) => [createQueryKey("getFields", options)]; +export const getFieldsQueryKey = (options?: Options) => [createQueryKey("getFields", options)]; -export const getFieldsOptions = (options?: Options) => { +export const getFieldsOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getFields({ @@ -212,9 +211,9 @@ export const getFieldsOptions = (options?: Options) => { }); }; -export const getCasesQueryKey = (options: Options) => [createQueryKey("getCases", options)]; +export const getCasesQueryKey = (options: Options) => [createQueryKey("getCases", options)]; -export const getCasesOptions = (options: Options) => { +export const getCasesOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getCases({ @@ -229,9 +228,9 @@ export const getCasesOptions = (options: Options) => { }); }; -export const getEnsemblesQueryKey = (options: Options) => [createQueryKey("getEnsembles", options)]; +export const getEnsemblesQueryKey = (options: Options) => [createQueryKey("getEnsembles", options)]; -export const getEnsemblesOptions = (options: Options) => { +export const getEnsemblesOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getEnsembles({ @@ -246,11 +245,11 @@ export const getEnsemblesOptions = (options: Options) => { }); }; -export const getEnsembleDetailsQueryKey = (options: Options) => [ +export const getEnsembleDetailsQueryKey = (options: Options) => [ createQueryKey("getEnsembleDetails", options), ]; -export const getEnsembleDetailsOptions = (options: Options) => { +export const getEnsembleDetailsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getEnsembleDetails({ @@ -265,11 +264,11 @@ export const getEnsembleDetailsOptions = (options: Options) => [ +export const getVectorListQueryKey = (options: Options) => [ createQueryKey("getVectorList", options), ]; -export const getVectorListOptions = (options: Options) => { +export const getVectorListOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getVectorList({ @@ -284,11 +283,11 @@ export const getVectorListOptions = (options: Options) => }); }; -export const getDeltaEnsembleVectorListQueryKey = (options: Options) => [ +export const getDeltaEnsembleVectorListQueryKey = (options: Options) => [ createQueryKey("getDeltaEnsembleVectorList", options), ]; -export const getDeltaEnsembleVectorListOptions = (options: Options) => { +export const getDeltaEnsembleVectorListOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getDeltaEnsembleVectorList({ @@ -303,11 +302,11 @@ export const getDeltaEnsembleVectorListOptions = (options: Options) => [ +export const getRealizationsVectorDataQueryKey = (options: Options) => [ createQueryKey("getRealizationsVectorData", options), ]; -export const getRealizationsVectorDataOptions = (options: Options) => { +export const getRealizationsVectorDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getRealizationsVectorData({ @@ -323,11 +322,11 @@ export const getRealizationsVectorDataOptions = (options: Options, + options: Options, ) => [createQueryKey("getDeltaEnsembleRealizationsVectorData", options)]; export const getDeltaEnsembleRealizationsVectorDataOptions = ( - options: Options, + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { @@ -343,11 +342,11 @@ export const getDeltaEnsembleRealizationsVectorDataOptions = ( }); }; -export const getTimestampsListQueryKey = (options: Options) => [ +export const getTimestampsListQueryKey = (options: Options) => [ createQueryKey("getTimestampsList", options), ]; -export const getTimestampsListOptions = (options: Options) => { +export const getTimestampsListOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getTimestampsList({ @@ -362,11 +361,11 @@ export const getTimestampsListOptions = (options: Options) => [ +export const getHistoricalVectorDataQueryKey = (options: Options) => [ createQueryKey("getHistoricalVectorData", options), ]; -export const getHistoricalVectorDataOptions = (options: Options) => { +export const getHistoricalVectorDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getHistoricalVectorData({ @@ -381,11 +380,11 @@ export const getHistoricalVectorDataOptions = (options: Options) => [ +export const getStatisticalVectorDataQueryKey = (options: Options) => [ createQueryKey("getStatisticalVectorData", options), ]; -export const getStatisticalVectorDataOptions = (options: Options) => { +export const getStatisticalVectorDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getStatisticalVectorData({ @@ -401,11 +400,11 @@ export const getStatisticalVectorDataOptions = (options: Options, + options: Options, ) => [createQueryKey("getDeltaEnsembleStatisticalVectorData", options)]; export const getDeltaEnsembleStatisticalVectorDataOptions = ( - options: Options, + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { @@ -422,11 +421,11 @@ export const getDeltaEnsembleStatisticalVectorDataOptions = ( }; export const getStatisticalVectorDataPerSensitivityQueryKey = ( - options: Options, + options: Options, ) => [createQueryKey("getStatisticalVectorDataPerSensitivity", options)]; export const getStatisticalVectorDataPerSensitivityOptions = ( - options: Options, + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { @@ -442,11 +441,11 @@ export const getStatisticalVectorDataPerSensitivityOptions = ( }); }; -export const getRealizationVectorAtTimestampQueryKey = (options: Options) => [ +export const getRealizationVectorAtTimestampQueryKey = (options: Options) => [ createQueryKey("getRealizationVectorAtTimestamp", options), ]; -export const getRealizationVectorAtTimestampOptions = (options: Options) => { +export const getRealizationVectorAtTimestampOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getRealizationVectorAtTimestamp({ @@ -461,11 +460,11 @@ export const getRealizationVectorAtTimestampOptions = (options: Options) => [ +export const getTableDefinitionsQueryKey = (options: Options) => [ createQueryKey("getTableDefinitions", options), ]; -export const getTableDefinitionsOptions = (options: Options) => { +export const getTableDefinitionsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getTableDefinitions({ @@ -481,11 +480,11 @@ export const getTableDefinitionsOptions = (options: Options, + options: Options, ) => [createQueryKey("postGetAggregatedPerRealizationTableData", options)]; export const postGetAggregatedPerRealizationTableDataOptions = ( - options: Options, + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { @@ -502,12 +501,12 @@ export const postGetAggregatedPerRealizationTableDataOptions = ( }; export const postGetAggregatedPerRealizationTableDataMutation = ( - options?: Partial>, + options?: Partial>, ) => { const mutationOptions: UseMutationOptions< - PostGetAggregatedPerRealizationTableDataResponse_api, - AxiosError, - Options + PostGetAggregatedPerRealizationTableDataResponse, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetAggregatedPerRealizationTableData({ @@ -522,11 +521,11 @@ export const postGetAggregatedPerRealizationTableDataMutation = ( }; export const postGetAggregatedStatisticalTableDataQueryKey = ( - options: Options, + options: Options, ) => [createQueryKey("postGetAggregatedStatisticalTableData", options)]; export const postGetAggregatedStatisticalTableDataOptions = ( - options: Options, + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { @@ -543,12 +542,12 @@ export const postGetAggregatedStatisticalTableDataOptions = ( }; export const postGetAggregatedStatisticalTableDataMutation = ( - options?: Partial>, + options?: Partial>, ) => { const mutationOptions: UseMutationOptions< - PostGetAggregatedStatisticalTableDataResponse_api, - AxiosError, - Options + PostGetAggregatedStatisticalTableDataResponse, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetAggregatedStatisticalTableData({ @@ -562,11 +561,11 @@ export const postGetAggregatedStatisticalTableDataMutation = ( return mutationOptions; }; -export const getRealizationSurfacesMetadataQueryKey = (options: Options) => [ +export const getRealizationSurfacesMetadataQueryKey = (options: Options) => [ createQueryKey("getRealizationSurfacesMetadata", options), ]; -export const getRealizationSurfacesMetadataOptions = (options: Options) => { +export const getRealizationSurfacesMetadataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getRealizationSurfacesMetadata({ @@ -581,11 +580,11 @@ export const getRealizationSurfacesMetadataOptions = (options: Options) => [ +export const getObservedSurfacesMetadataQueryKey = (options: Options) => [ createQueryKey("getObservedSurfacesMetadata", options), ]; -export const getObservedSurfacesMetadataOptions = (options: Options) => { +export const getObservedSurfacesMetadataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getObservedSurfacesMetadata({ @@ -600,11 +599,11 @@ export const getObservedSurfacesMetadataOptions = (options: Options) => [ +export const getSurfaceDataQueryKey = (options: Options) => [ createQueryKey("getSurfaceData", options), ]; -export const getSurfaceDataOptions = (options: Options) => { +export const getSurfaceDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getSurfaceData({ @@ -619,11 +618,11 @@ export const getSurfaceDataOptions = (options: Options) }); }; -export const postGetSurfaceIntersectionQueryKey = (options: Options) => [ +export const postGetSurfaceIntersectionQueryKey = (options: Options) => [ createQueryKey("postGetSurfaceIntersection", options), ]; -export const postGetSurfaceIntersectionOptions = (options: Options) => { +export const postGetSurfaceIntersectionOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await postGetSurfaceIntersection({ @@ -638,11 +637,11 @@ export const postGetSurfaceIntersectionOptions = (options: Options>) => { +export const postGetSurfaceIntersectionMutation = (options?: Partial>) => { const mutationOptions: UseMutationOptions< - PostGetSurfaceIntersectionResponse_api, - AxiosError, - Options + PostGetSurfaceIntersectionResponse, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetSurfaceIntersection({ @@ -656,11 +655,11 @@ export const postGetSurfaceIntersectionMutation = (options?: Partial) => [ +export const postGetSampleSurfaceInPointsQueryKey = (options: Options) => [ createQueryKey("postGetSampleSurfaceInPoints", options), ]; -export const postGetSampleSurfaceInPointsOptions = (options: Options) => { +export const postGetSampleSurfaceInPointsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await postGetSampleSurfaceInPoints({ @@ -675,11 +674,11 @@ export const postGetSampleSurfaceInPointsOptions = (options: Options>) => { +export const postGetSampleSurfaceInPointsMutation = (options?: Partial>) => { const mutationOptions: UseMutationOptions< - PostGetSampleSurfaceInPointsResponse_api, - AxiosError, - Options + PostGetSampleSurfaceInPointsResponse, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetSampleSurfaceInPoints({ @@ -693,11 +692,11 @@ export const postGetSampleSurfaceInPointsMutation = (options?: Partial) => [ +export const getDeltaSurfaceDataQueryKey = (options: Options) => [ createQueryKey("getDeltaSurfaceData", options), ]; -export const getDeltaSurfaceDataOptions = (options: Options) => { +export const getDeltaSurfaceDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getDeltaSurfaceData({ @@ -712,11 +711,11 @@ export const getDeltaSurfaceDataOptions = (options: Options) => [ +export const getMisfitSurfaceDataQueryKey = (options: Options) => [ createQueryKey("getMisfitSurfaceData", options), ]; -export const getMisfitSurfaceDataOptions = (options: Options) => { +export const getMisfitSurfaceDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getMisfitSurfaceData({ @@ -731,11 +730,11 @@ export const getMisfitSurfaceDataOptions = (options: Options) => [ +export const getWellboreStratigraphicColumnsQueryKey = (options: Options) => [ createQueryKey("getWellboreStratigraphicColumns", options), ]; -export const getWellboreStratigraphicColumnsOptions = (options: Options) => { +export const getWellboreStratigraphicColumnsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellboreStratigraphicColumns({ @@ -750,11 +749,11 @@ export const getWellboreStratigraphicColumnsOptions = (options: Options) => [ +export const getStratigraphicUnitsQueryKey = (options: Options) => [ createQueryKey("getStratigraphicUnits", options), ]; -export const getStratigraphicUnitsOptions = (options: Options) => { +export const getStratigraphicUnitsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getStratigraphicUnits({ @@ -769,11 +768,11 @@ export const getStratigraphicUnitsOptions = (options: Options) => [ +export const getParameterNamesAndDescriptionQueryKey = (options: Options) => [ createQueryKey("getParameterNamesAndDescription", options), ]; -export const getParameterNamesAndDescriptionOptions = (options: Options) => { +export const getParameterNamesAndDescriptionOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getParameterNamesAndDescription({ @@ -788,9 +787,9 @@ export const getParameterNamesAndDescriptionOptions = (options: Options) => [createQueryKey("getParameter", options)]; +export const getParameterQueryKey = (options: Options) => [createQueryKey("getParameter", options)]; -export const getParameterOptions = (options: Options) => { +export const getParameterOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getParameter({ @@ -805,11 +804,11 @@ export const getParameterOptions = (options: Options) => { }); }; -export const getParametersQueryKey = (options: Options) => [ +export const getParametersQueryKey = (options: Options) => [ createQueryKey("getParameters", options), ]; -export const getParametersOptions = (options: Options) => { +export const getParametersOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getParameters({ @@ -824,11 +823,11 @@ export const getParametersOptions = (options: Options) => }); }; -export const getIsSensitivityRunQueryKey = (options: Options) => [ +export const getIsSensitivityRunQueryKey = (options: Options) => [ createQueryKey("getIsSensitivityRun", options), ]; -export const getIsSensitivityRunOptions = (options: Options) => { +export const getIsSensitivityRunOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getIsSensitivityRun({ @@ -843,11 +842,11 @@ export const getIsSensitivityRunOptions = (options: Options) => [ +export const getSensitivitiesQueryKey = (options: Options) => [ createQueryKey("getSensitivities", options), ]; -export const getSensitivitiesOptions = (options: Options) => { +export const getSensitivitiesOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getSensitivities({ @@ -862,11 +861,11 @@ export const getSensitivitiesOptions = (options: Options) => [ +export const getGridModelsInfoQueryKey = (options: Options) => [ createQueryKey("getGridModelsInfo", options), ]; -export const getGridModelsInfoOptions = (options: Options) => { +export const getGridModelsInfoOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getGridModelsInfo({ @@ -881,11 +880,11 @@ export const getGridModelsInfoOptions = (options: Options) => [ +export const getGridSurfaceQueryKey = (options: Options) => [ createQueryKey("getGridSurface", options), ]; -export const getGridSurfaceOptions = (options: Options) => { +export const getGridSurfaceOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getGridSurface({ @@ -900,11 +899,11 @@ export const getGridSurfaceOptions = (options: Options) }); }; -export const getGridParameterQueryKey = (options: Options) => [ +export const getGridParameterQueryKey = (options: Options) => [ createQueryKey("getGridParameter", options), ]; -export const getGridParameterOptions = (options: Options) => { +export const getGridParameterOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getGridParameter({ @@ -919,11 +918,11 @@ export const getGridParameterOptions = (options: Options) => [ +export const postGetPolylineIntersectionQueryKey = (options: Options) => [ createQueryKey("postGetPolylineIntersection", options), ]; -export const postGetPolylineIntersectionOptions = (options: Options) => { +export const postGetPolylineIntersectionOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await postGetPolylineIntersection({ @@ -938,11 +937,11 @@ export const postGetPolylineIntersectionOptions = (options: Options>) => { +export const postGetPolylineIntersectionMutation = (options?: Partial>) => { const mutationOptions: UseMutationOptions< - PostGetPolylineIntersectionResponse_api, - AxiosError, - Options + PostGetPolylineIntersectionResponse, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetPolylineIntersection({ @@ -956,11 +955,11 @@ export const postGetPolylineIntersectionMutation = (options?: Partial) => [ +export const getRealizationFlowNetworkQueryKey = (options: Options) => [ createQueryKey("getRealizationFlowNetwork", options), ]; -export const getRealizationFlowNetworkOptions = (options: Options) => { +export const getRealizationFlowNetworkOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getRealizationFlowNetwork({ @@ -975,9 +974,9 @@ export const getRealizationFlowNetworkOptions = (options: Options) => [createQueryKey("getTableData", options)]; +export const getTableDataQueryKey = (options: Options) => [createQueryKey("getTableData", options)]; -export const getTableDataOptions = (options: Options) => { +export const getTableDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getTableData({ @@ -992,11 +991,11 @@ export const getTableDataOptions = (options: Options) => { }); }; -export const getWellCompletionsDataQueryKey = (options: Options) => [ +export const getWellCompletionsDataQueryKey = (options: Options) => [ createQueryKey("getWellCompletionsData", options), ]; -export const getWellCompletionsDataOptions = (options: Options) => { +export const getWellCompletionsDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellCompletionsData({ @@ -1011,11 +1010,11 @@ export const getWellCompletionsDataOptions = (options: Options) => [ +export const getDrilledWellboreHeadersQueryKey = (options: Options) => [ createQueryKey("getDrilledWellboreHeaders", options), ]; -export const getDrilledWellboreHeadersOptions = (options: Options) => { +export const getDrilledWellboreHeadersOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getDrilledWellboreHeaders({ @@ -1030,11 +1029,11 @@ export const getDrilledWellboreHeadersOptions = (options: Options) => [ +export const getWellTrajectoriesQueryKey = (options: Options) => [ createQueryKey("getWellTrajectories", options), ]; -export const getWellTrajectoriesOptions = (options: Options) => { +export const getWellTrajectoriesOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellTrajectories({ @@ -1049,11 +1048,11 @@ export const getWellTrajectoriesOptions = (options: Options) => [ +export const getWellborePickIdentifiersQueryKey = (options: Options) => [ createQueryKey("getWellborePickIdentifiers", options), ]; -export const getWellborePickIdentifiersOptions = (options: Options) => { +export const getWellborePickIdentifiersOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellborePickIdentifiers({ @@ -1068,11 +1067,11 @@ export const getWellborePickIdentifiersOptions = (options: Options) => [ +export const getWellborePicksForPickIdentifierQueryKey = (options: Options) => [ createQueryKey("getWellborePicksForPickIdentifier", options), ]; -export const getWellborePicksForPickIdentifierOptions = (options: Options) => { +export const getWellborePicksForPickIdentifierOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellborePicksForPickIdentifier({ @@ -1087,11 +1086,11 @@ export const getWellborePicksForPickIdentifierOptions = (options: Options) => [ +export const getWellborePicksForWellboreQueryKey = (options: Options) => [ createQueryKey("getWellborePicksForWellbore", options), ]; -export const getWellborePicksForWellboreOptions = (options: Options) => { +export const getWellborePicksForWellboreOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellborePicksForWellbore({ @@ -1106,11 +1105,11 @@ export const getWellborePicksForWellboreOptions = (options: Options) => [ +export const getWellborePicksInStratColumnQueryKey = (options: Options) => [ createQueryKey("getWellborePicksInStratColumn", options), ]; -export const getWellborePicksInStratColumnOptions = (options: Options) => { +export const getWellborePicksInStratColumnOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellborePicksInStratColumn({ @@ -1125,11 +1124,11 @@ export const getWellborePicksInStratColumnOptions = (options: Options) => [ +export const getWellboreCompletionsQueryKey = (options: Options) => [ createQueryKey("getWellboreCompletions", options), ]; -export const getWellboreCompletionsOptions = (options: Options) => { +export const getWellboreCompletionsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellboreCompletions({ @@ -1144,11 +1143,11 @@ export const getWellboreCompletionsOptions = (options: Options) => [ +export const getWellboreCasingsQueryKey = (options: Options) => [ createQueryKey("getWellboreCasings", options), ]; -export const getWellboreCasingsOptions = (options: Options) => { +export const getWellboreCasingsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellboreCasings({ @@ -1163,11 +1162,11 @@ export const getWellboreCasingsOptions = (options: Options) => [ +export const getWellborePerforationsQueryKey = (options: Options) => [ createQueryKey("getWellborePerforations", options), ]; -export const getWellborePerforationsOptions = (options: Options) => { +export const getWellborePerforationsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellborePerforations({ @@ -1182,11 +1181,11 @@ export const getWellborePerforationsOptions = (options: Options) => [ +export const getWellboreLogCurveHeadersQueryKey = (options: Options) => [ createQueryKey("getWellboreLogCurveHeaders", options), ]; -export const getWellboreLogCurveHeadersOptions = (options: Options) => { +export const getWellboreLogCurveHeadersOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellboreLogCurveHeaders({ @@ -1201,11 +1200,11 @@ export const getWellboreLogCurveHeadersOptions = (options: Options) => [ +export const getLogCurveDataQueryKey = (options: Options) => [ createQueryKey("getLogCurveData", options), ]; -export const getLogCurveDataOptions = (options: Options) => { +export const getLogCurveDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getLogCurveData({ @@ -1220,11 +1219,11 @@ export const getLogCurveDataOptions = (options: Options }); }; -export const getSeismicCubeMetaListQueryKey = (options: Options) => [ +export const getSeismicCubeMetaListQueryKey = (options: Options) => [ createQueryKey("getSeismicCubeMetaList", options), ]; -export const getSeismicCubeMetaListOptions = (options: Options) => { +export const getSeismicCubeMetaListOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getSeismicCubeMetaList({ @@ -1239,11 +1238,11 @@ export const getSeismicCubeMetaListOptions = (options: Options) => [ +export const getInlineSliceQueryKey = (options: Options) => [ createQueryKey("getInlineSlice", options), ]; -export const getInlineSliceOptions = (options: Options) => { +export const getInlineSliceOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getInlineSlice({ @@ -1258,11 +1257,11 @@ export const getInlineSliceOptions = (options: Options) }); }; -export const getCrosslineSliceQueryKey = (options: Options) => [ +export const getCrosslineSliceQueryKey = (options: Options) => [ createQueryKey("getCrosslineSlice", options), ]; -export const getCrosslineSliceOptions = (options: Options) => { +export const getCrosslineSliceOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getCrosslineSlice({ @@ -1277,11 +1276,11 @@ export const getCrosslineSliceOptions = (options: Options) => [ +export const getDepthSliceQueryKey = (options: Options) => [ createQueryKey("getDepthSlice", options), ]; -export const getDepthSliceOptions = (options: Options) => { +export const getDepthSliceOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getDepthSlice({ @@ -1296,11 +1295,11 @@ export const getDepthSliceOptions = (options: Options) => }); }; -export const postGetSeismicFenceQueryKey = (options: Options) => [ +export const postGetSeismicFenceQueryKey = (options: Options) => [ createQueryKey("postGetSeismicFence", options), ]; -export const postGetSeismicFenceOptions = (options: Options) => { +export const postGetSeismicFenceOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await postGetSeismicFence({ @@ -1315,11 +1314,11 @@ export const postGetSeismicFenceOptions = (options: Options>) => { +export const postGetSeismicFenceMutation = (options?: Partial>) => { const mutationOptions: UseMutationOptions< - PostGetSeismicFenceResponse_api, - AxiosError, - Options + PostGetSeismicFenceResponse, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetSeismicFence({ @@ -1333,11 +1332,11 @@ export const postGetSeismicFenceMutation = (options?: Partial) => [ +export const getPolygonsDirectoryQueryKey = (options: Options) => [ createQueryKey("getPolygonsDirectory", options), ]; -export const getPolygonsDirectoryOptions = (options: Options) => { +export const getPolygonsDirectoryOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getPolygonsDirectory({ @@ -1352,11 +1351,11 @@ export const getPolygonsDirectoryOptions = (options: Options) => [ +export const getPolygonsDataQueryKey = (options: Options) => [ createQueryKey("getPolygonsData", options), ]; -export const getPolygonsDataOptions = (options: Options) => { +export const getPolygonsDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getPolygonsData({ @@ -1371,9 +1370,9 @@ export const getPolygonsDataOptions = (options: Options }); }; -export const getUserPhotoQueryKey = (options: Options) => [createQueryKey("getUserPhoto", options)]; +export const getUserPhotoQueryKey = (options: Options) => [createQueryKey("getUserPhoto", options)]; -export const getUserPhotoOptions = (options: Options) => { +export const getUserPhotoOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getUserPhoto({ @@ -1388,11 +1387,11 @@ export const getUserPhotoOptions = (options: Options) => { }); }; -export const getObservationsQueryKey = (options: Options) => [ +export const getObservationsQueryKey = (options: Options) => [ createQueryKey("getObservations", options), ]; -export const getObservationsOptions = (options: Options) => { +export const getObservationsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getObservations({ @@ -1407,11 +1406,11 @@ export const getObservationsOptions = (options: Options }); }; -export const getTableDefinitionQueryKey = (options: Options) => [ +export const getTableDefinitionQueryKey = (options: Options) => [ createQueryKey("getTableDefinition", options), ]; -export const getTableDefinitionOptions = (options: Options) => { +export const getTableDefinitionOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getTableDefinition({ @@ -1426,11 +1425,11 @@ export const getTableDefinitionOptions = (options: Options) => [ +export const getRealizationDataQueryKey = (options: Options) => [ createQueryKey("getRealizationData", options), ]; -export const getRealizationDataOptions = (options: Options) => { +export const getRealizationDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getRealizationData({ @@ -1445,11 +1444,11 @@ export const getRealizationDataOptions = (options: Options) => [ +export const getVfpTableNamesQueryKey = (options: Options) => [ createQueryKey("getVfpTableNames", options), ]; -export const getVfpTableNamesOptions = (options: Options) => { +export const getVfpTableNamesOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getVfpTableNames({ @@ -1464,9 +1463,9 @@ export const getVfpTableNamesOptions = (options: Options) => [createQueryKey("getVfpTable", options)]; +export const getVfpTableQueryKey = (options: Options) => [createQueryKey("getVfpTable", options)]; -export const getVfpTableOptions = (options: Options) => { +export const getVfpTableOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getVfpTable({ @@ -1481,9 +1480,9 @@ export const getVfpTableOptions = (options: Options) => { }); }; -export const loginRouteQueryKey = (options?: Options) => [createQueryKey("loginRoute", options)]; +export const loginRouteQueryKey = (options?: Options) => [createQueryKey("loginRoute", options)]; -export const loginRouteOptions = (options?: Options) => { +export const loginRouteOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await loginRoute({ @@ -1498,11 +1497,11 @@ export const loginRouteOptions = (options?: Options) => { }); }; -export const authorizedCallbackRouteQueryKey = (options?: Options) => [ +export const authorizedCallbackRouteQueryKey = (options?: Options) => [ createQueryKey("authorizedCallbackRoute", options), ]; -export const authorizedCallbackRouteOptions = (options?: Options) => { +export const authorizedCallbackRouteOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await authorizedCallbackRoute({ @@ -1517,9 +1516,9 @@ export const authorizedCallbackRouteOptions = (options?: Options) => [createQueryKey("getAlive", options)]; +export const getAliveQueryKey = (options?: Options) => [createQueryKey("getAlive", options)]; -export const getAliveOptions = (options?: Options) => { +export const getAliveOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getAlive({ @@ -1534,11 +1533,11 @@ export const getAliveOptions = (options?: Options) => { }); }; -export const getAliveProtectedQueryKey = (options?: Options) => [ +export const getAliveProtectedQueryKey = (options?: Options) => [ createQueryKey("getAliveProtected", options), ]; -export const getAliveProtectedOptions = (options?: Options) => { +export const getAliveProtectedOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getAliveProtected({ @@ -1553,9 +1552,9 @@ export const getAliveProtectedOptions = (options?: Options) => [createQueryKey("postLogout", options)]; +export const postLogoutQueryKey = (options?: Options) => [createQueryKey("postLogout", options)]; -export const postLogoutOptions = (options?: Options) => { +export const postLogoutOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await postLogout({ @@ -1570,8 +1569,8 @@ export const postLogoutOptions = (options?: Options) => { }); }; -export const postLogoutMutation = (options?: Partial>) => { - const mutationOptions: UseMutationOptions, Options> = { +export const postLogoutMutation = (options?: Partial>) => { + const mutationOptions: UseMutationOptions, Options> = { mutationFn: async (localOptions) => { const { data } = await postLogout({ ...options, @@ -1584,11 +1583,11 @@ export const postLogoutMutation = (options?: Partial return mutationOptions; }; -export const getLoggedInUserQueryKey = (options?: Options) => [ +export const getLoggedInUserQueryKey = (options?: Options) => [ createQueryKey("getLoggedInUser", options), ]; -export const getLoggedInUserOptions = (options?: Options) => { +export const getLoggedInUserOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getLoggedInUser({ @@ -1603,9 +1602,9 @@ export const getLoggedInUserOptions = (options?: Options) => [createQueryKey("root", options)]; +export const rootQueryKey = (options?: Options) => [createQueryKey("root", options)]; -export const rootOptions = (options?: Options) => { +export const rootOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await root({ diff --git a/frontend/src/api/autogen/index.ts b/frontend/src/api/autogen/index.ts index bcf1aa9cc..da8707936 100644 --- a/frontend/src/api/autogen/index.ts +++ b/frontend/src/api/autogen/index.ts @@ -1,5 +1,3 @@ // This file is auto-generated by @hey-api/openapi-ts export * from "./types.gen"; export * from "./sdk.gen"; - -export * from './@tanstack/react-query.gen'; diff --git a/frontend/src/api/autogen/sdk.gen.ts b/frontend/src/api/autogen/sdk.gen.ts index 984ab25f5..df38a1c98 100644 --- a/frontend/src/api/autogen/sdk.gen.ts +++ b/frontend/src/api/autogen/sdk.gen.ts @@ -1,207 +1,206 @@ // This file is auto-generated by @hey-api/openapi-ts import { createClient, createConfig, type Options } from "@hey-api/client-axios"; - import type { - GetFieldsData_api, - GetFieldsResponse_api, - GetCasesData_api, - GetCasesResponse_api, - GetCasesError_api, - GetEnsemblesData_api, - GetEnsemblesResponse_api, - GetEnsemblesError_api, - GetEnsembleDetailsData_api, - GetEnsembleDetailsResponse_api, - GetEnsembleDetailsError_api, - GetVectorListData_api, - GetVectorListResponse_api, - GetVectorListError_api, - GetDeltaEnsembleVectorListData_api, - GetDeltaEnsembleVectorListResponse_api, - GetDeltaEnsembleVectorListError_api, - GetRealizationsVectorDataData_api, - GetRealizationsVectorDataResponse_api, - GetRealizationsVectorDataError_api, - GetDeltaEnsembleRealizationsVectorDataData_api, - GetDeltaEnsembleRealizationsVectorDataResponse_api, - GetDeltaEnsembleRealizationsVectorDataError_api, - GetTimestampsListData_api, - GetTimestampsListResponse_api, - GetTimestampsListError_api, - GetHistoricalVectorDataData_api, - GetHistoricalVectorDataResponse_api, - GetHistoricalVectorDataError_api, - GetStatisticalVectorDataData_api, - GetStatisticalVectorDataResponse_api, - GetStatisticalVectorDataError_api, - GetDeltaEnsembleStatisticalVectorDataData_api, - GetDeltaEnsembleStatisticalVectorDataResponse_api, - GetDeltaEnsembleStatisticalVectorDataError_api, - GetStatisticalVectorDataPerSensitivityData_api, - GetStatisticalVectorDataPerSensitivityResponse_api, - GetStatisticalVectorDataPerSensitivityError_api, - GetRealizationVectorAtTimestampData_api, - GetRealizationVectorAtTimestampResponse_api, - GetRealizationVectorAtTimestampError_api, - GetTableDefinitionsData_api, - GetTableDefinitionsResponse_api, - GetTableDefinitionsError_api, - PostGetAggregatedPerRealizationTableDataData_api, - PostGetAggregatedPerRealizationTableDataResponse_api, - PostGetAggregatedPerRealizationTableDataError_api, - PostGetAggregatedStatisticalTableDataData_api, - PostGetAggregatedStatisticalTableDataResponse_api, - PostGetAggregatedStatisticalTableDataError_api, - GetRealizationSurfacesMetadataData_api, - GetRealizationSurfacesMetadataResponse_api, - GetRealizationSurfacesMetadataError_api, - GetObservedSurfacesMetadataData_api, - GetObservedSurfacesMetadataResponse_api, - GetObservedSurfacesMetadataError_api, - GetSurfaceDataData_api, - GetSurfaceDataResponse_api, - GetSurfaceDataError_api, - PostGetSurfaceIntersectionData_api, - PostGetSurfaceIntersectionResponse_api, - PostGetSurfaceIntersectionError_api, - PostGetSampleSurfaceInPointsData_api, - PostGetSampleSurfaceInPointsResponse_api, - PostGetSampleSurfaceInPointsError_api, - GetDeltaSurfaceDataData_api, - GetDeltaSurfaceDataResponse_api, - GetDeltaSurfaceDataError_api, - GetMisfitSurfaceDataData_api, - GetMisfitSurfaceDataResponse_api, - GetMisfitSurfaceDataError_api, - GetWellboreStratigraphicColumnsData_api, - GetWellboreStratigraphicColumnsResponse_api, - GetWellboreStratigraphicColumnsError_api, - GetStratigraphicUnitsData_api, - GetStratigraphicUnitsResponse_api, - GetStratigraphicUnitsError_api, - GetParameterNamesAndDescriptionData_api, - GetParameterNamesAndDescriptionResponse_api, - GetParameterNamesAndDescriptionError_api, - GetParameterData_api, - GetParameterResponse_api, - GetParameterError_api, - GetParametersData_api, - GetParametersResponse_api, - GetParametersError_api, - GetIsSensitivityRunData_api, - GetIsSensitivityRunResponse_api, - GetIsSensitivityRunError_api, - GetSensitivitiesData_api, - GetSensitivitiesResponse_api, - GetSensitivitiesError_api, - GetGridModelsInfoData_api, - GetGridModelsInfoResponse_api, - GetGridModelsInfoError_api, - GetGridSurfaceData_api, - GetGridSurfaceResponse_api, - GetGridSurfaceError_api, - GetGridParameterData_api, - GetGridParameterResponse_api, - GetGridParameterError_api, - PostGetPolylineIntersectionData_api, - PostGetPolylineIntersectionResponse_api, - PostGetPolylineIntersectionError_api, - GetRealizationFlowNetworkData_api, - GetRealizationFlowNetworkResponse_api, - GetRealizationFlowNetworkError_api, - GetTableDataData_api, - GetTableDataResponse_api, - GetTableDataError_api, - GetWellCompletionsDataData_api, - GetWellCompletionsDataResponse_api, - GetWellCompletionsDataError_api, - GetDrilledWellboreHeadersData_api, - GetDrilledWellboreHeadersResponse_api, - GetDrilledWellboreHeadersError_api, - GetWellTrajectoriesData_api, - GetWellTrajectoriesResponse_api, - GetWellTrajectoriesError_api, - GetWellborePickIdentifiersData_api, - GetWellborePickIdentifiersResponse_api, - GetWellborePickIdentifiersError_api, - GetWellborePicksForPickIdentifierData_api, - GetWellborePicksForPickIdentifierResponse_api, - GetWellborePicksForPickIdentifierError_api, - GetWellborePicksForWellboreData_api, - GetWellborePicksForWellboreResponse_api, - GetWellborePicksForWellboreError_api, - GetWellborePicksInStratColumnData_api, - GetWellborePicksInStratColumnResponse_api, - GetWellborePicksInStratColumnError_api, - GetWellboreCompletionsData_api, - GetWellboreCompletionsResponse_api, - GetWellboreCompletionsError_api, - GetWellboreCasingsData_api, - GetWellboreCasingsResponse_api, - GetWellboreCasingsError_api, - GetWellborePerforationsData_api, - GetWellborePerforationsResponse_api, - GetWellborePerforationsError_api, - GetWellboreLogCurveHeadersData_api, - GetWellboreLogCurveHeadersResponse_api, - GetWellboreLogCurveHeadersError_api, - GetLogCurveDataData_api, - GetLogCurveDataResponse_api, - GetLogCurveDataError_api, - GetSeismicCubeMetaListData_api, - GetSeismicCubeMetaListResponse_api, - GetSeismicCubeMetaListError_api, - GetInlineSliceData_api, - GetInlineSliceResponse_api, - GetInlineSliceError_api, - GetCrosslineSliceData_api, - GetCrosslineSliceResponse_api, - GetCrosslineSliceError_api, - GetDepthSliceData_api, - GetDepthSliceResponse_api, - GetDepthSliceError_api, - PostGetSeismicFenceData_api, - PostGetSeismicFenceResponse_api, - PostGetSeismicFenceError_api, - GetPolygonsDirectoryData_api, - GetPolygonsDirectoryResponse_api, - GetPolygonsDirectoryError_api, - GetPolygonsDataData_api, - GetPolygonsDataResponse_api, - GetPolygonsDataError_api, - GetUserPhotoData_api, - GetUserPhotoResponse_api, - GetUserPhotoError_api, - GetObservationsData_api, - GetObservationsResponse_api, - GetObservationsError_api, - GetTableDefinitionData_api, - GetTableDefinitionResponse_api, - GetTableDefinitionError_api, - GetRealizationDataData_api, - GetRealizationDataResponse_api, - GetRealizationDataError_api, - GetVfpTableNamesData_api, - GetVfpTableNamesResponse_api, - GetVfpTableNamesError_api, - GetVfpTableData_api, - GetVfpTableResponse_api, - GetVfpTableError_api, - LoginRouteData_api, - LoginRouteError_api, - AuthorizedCallbackRouteData_api, - GetAliveData_api, - GetAliveResponse_api, - GetAliveProtectedData_api, - GetAliveProtectedResponse_api, - PostLogoutData_api, - PostLogoutResponse_api, - GetLoggedInUserData_api, - GetLoggedInUserResponse_api, - GetLoggedInUserError_api, - RootData_api, - RootResponse_api, + GetFieldsData, + GetFieldsResponse, + GetCasesData, + GetCasesResponse, + GetCasesError, + GetEnsemblesData, + GetEnsemblesResponse, + GetEnsemblesError, + GetEnsembleDetailsData, + GetEnsembleDetailsResponse, + GetEnsembleDetailsError, + GetVectorListData, + GetVectorListResponse, + GetVectorListError, + GetDeltaEnsembleVectorListData, + GetDeltaEnsembleVectorListResponse, + GetDeltaEnsembleVectorListError, + GetRealizationsVectorDataData, + GetRealizationsVectorDataResponse, + GetRealizationsVectorDataError, + GetDeltaEnsembleRealizationsVectorDataData, + GetDeltaEnsembleRealizationsVectorDataResponse, + GetDeltaEnsembleRealizationsVectorDataError, + GetTimestampsListData, + GetTimestampsListResponse, + GetTimestampsListError, + GetHistoricalVectorDataData, + GetHistoricalVectorDataResponse, + GetHistoricalVectorDataError, + GetStatisticalVectorDataData, + GetStatisticalVectorDataResponse, + GetStatisticalVectorDataError, + GetDeltaEnsembleStatisticalVectorDataData, + GetDeltaEnsembleStatisticalVectorDataResponse, + GetDeltaEnsembleStatisticalVectorDataError, + GetStatisticalVectorDataPerSensitivityData, + GetStatisticalVectorDataPerSensitivityResponse, + GetStatisticalVectorDataPerSensitivityError, + GetRealizationVectorAtTimestampData, + GetRealizationVectorAtTimestampResponse, + GetRealizationVectorAtTimestampError, + GetTableDefinitionsData, + GetTableDefinitionsResponse, + GetTableDefinitionsError, + PostGetAggregatedPerRealizationTableDataData, + PostGetAggregatedPerRealizationTableDataResponse, + PostGetAggregatedPerRealizationTableDataError, + PostGetAggregatedStatisticalTableDataData, + PostGetAggregatedStatisticalTableDataResponse, + PostGetAggregatedStatisticalTableDataError, + GetRealizationSurfacesMetadataData, + GetRealizationSurfacesMetadataResponse, + GetRealizationSurfacesMetadataError, + GetObservedSurfacesMetadataData, + GetObservedSurfacesMetadataResponse, + GetObservedSurfacesMetadataError, + GetSurfaceDataData, + GetSurfaceDataResponse, + GetSurfaceDataError, + PostGetSurfaceIntersectionData, + PostGetSurfaceIntersectionResponse, + PostGetSurfaceIntersectionError, + PostGetSampleSurfaceInPointsData, + PostGetSampleSurfaceInPointsResponse, + PostGetSampleSurfaceInPointsError, + GetDeltaSurfaceDataData, + GetDeltaSurfaceDataResponse, + GetDeltaSurfaceDataError, + GetMisfitSurfaceDataData, + GetMisfitSurfaceDataResponse, + GetMisfitSurfaceDataError, + GetWellboreStratigraphicColumnsData, + GetWellboreStratigraphicColumnsResponse, + GetWellboreStratigraphicColumnsError, + GetStratigraphicUnitsData, + GetStratigraphicUnitsResponse, + GetStratigraphicUnitsError, + GetParameterNamesAndDescriptionData, + GetParameterNamesAndDescriptionResponse, + GetParameterNamesAndDescriptionError, + GetParameterData, + GetParameterResponse, + GetParameterError, + GetParametersData, + GetParametersResponse, + GetParametersError, + GetIsSensitivityRunData, + GetIsSensitivityRunResponse, + GetIsSensitivityRunError, + GetSensitivitiesData, + GetSensitivitiesResponse, + GetSensitivitiesError, + GetGridModelsInfoData, + GetGridModelsInfoResponse, + GetGridModelsInfoError, + GetGridSurfaceData, + GetGridSurfaceResponse, + GetGridSurfaceError, + GetGridParameterData, + GetGridParameterResponse, + GetGridParameterError, + PostGetPolylineIntersectionData, + PostGetPolylineIntersectionResponse, + PostGetPolylineIntersectionError, + GetRealizationFlowNetworkData, + GetRealizationFlowNetworkResponse, + GetRealizationFlowNetworkError, + GetTableDataData, + GetTableDataResponse, + GetTableDataError, + GetWellCompletionsDataData, + GetWellCompletionsDataResponse, + GetWellCompletionsDataError, + GetDrilledWellboreHeadersData, + GetDrilledWellboreHeadersResponse, + GetDrilledWellboreHeadersError, + GetWellTrajectoriesData, + GetWellTrajectoriesResponse, + GetWellTrajectoriesError, + GetWellborePickIdentifiersData, + GetWellborePickIdentifiersResponse, + GetWellborePickIdentifiersError, + GetWellborePicksForPickIdentifierData, + GetWellborePicksForPickIdentifierResponse, + GetWellborePicksForPickIdentifierError, + GetWellborePicksForWellboreData, + GetWellborePicksForWellboreResponse, + GetWellborePicksForWellboreError, + GetWellborePicksInStratColumnData, + GetWellborePicksInStratColumnResponse, + GetWellborePicksInStratColumnError, + GetWellboreCompletionsData, + GetWellboreCompletionsResponse, + GetWellboreCompletionsError, + GetWellboreCasingsData, + GetWellboreCasingsResponse, + GetWellboreCasingsError, + GetWellborePerforationsData, + GetWellborePerforationsResponse, + GetWellborePerforationsError, + GetWellboreLogCurveHeadersData, + GetWellboreLogCurveHeadersResponse, + GetWellboreLogCurveHeadersError, + GetLogCurveDataData, + GetLogCurveDataResponse, + GetLogCurveDataError, + GetSeismicCubeMetaListData, + GetSeismicCubeMetaListResponse, + GetSeismicCubeMetaListError, + GetInlineSliceData, + GetInlineSliceResponse, + GetInlineSliceError, + GetCrosslineSliceData, + GetCrosslineSliceResponse, + GetCrosslineSliceError, + GetDepthSliceData, + GetDepthSliceResponse, + GetDepthSliceError, + PostGetSeismicFenceData, + PostGetSeismicFenceResponse, + PostGetSeismicFenceError, + GetPolygonsDirectoryData, + GetPolygonsDirectoryResponse, + GetPolygonsDirectoryError, + GetPolygonsDataData, + GetPolygonsDataResponse, + GetPolygonsDataError, + GetUserPhotoData, + GetUserPhotoResponse, + GetUserPhotoError, + GetObservationsData, + GetObservationsResponse, + GetObservationsError, + GetTableDefinitionData, + GetTableDefinitionResponse, + GetTableDefinitionError, + GetRealizationDataData, + GetRealizationDataResponse, + GetRealizationDataError, + GetVfpTableNamesData, + GetVfpTableNamesResponse, + GetVfpTableNamesError, + GetVfpTableData, + GetVfpTableResponse, + GetVfpTableError, + LoginRouteData, + LoginRouteError, + AuthorizedCallbackRouteData, + GetAliveData, + GetAliveResponse, + GetAliveProtectedData, + GetAliveProtectedResponse, + PostLogoutData, + PostLogoutResponse, + GetLoggedInUserData, + GetLoggedInUserResponse, + GetLoggedInUserError, + RootData, + RootResponse, } from "./types.gen"; export const client = createClient(createConfig()); @@ -210,8 +209,8 @@ export const client = createClient(createConfig()); * Get Fields * Get list of fields */ -export const getFields = (options?: Options) => { - return (options?.client ?? client).get({ +export const getFields = (options?: Options) => { + return (options?.client ?? client).get({ ...options, url: "/fields", }); @@ -221,8 +220,8 @@ export const getFields = (options?: Option * Get Cases * Get list of cases for specified field */ -export const getCases = (options: Options) => { - return (options?.client ?? client).get({ +export const getCases = (options: Options) => { + return (options?.client ?? client).get({ ...options, url: "/cases", }); @@ -233,9 +232,9 @@ export const getCases = (options: Options< * Get list of ensembles for a case */ export const getEnsembles = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/cases/{case_uuid}/ensembles", }); @@ -246,9 +245,9 @@ export const getEnsembles = ( * Get more detailed information for an ensemble */ export const getEnsembleDetails = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/cases/{case_uuid}/ensembles/{ensemble_name}", }); @@ -261,9 +260,9 @@ export const getEnsembleDetails = ( * Optionally include derived vectors. */ export const getVectorList = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/timeseries/vector_list/", }); @@ -278,11 +277,11 @@ export const getVectorList = ( * delta_ensemble = comparison_ensemble - reference_ensemble */ export const getDeltaEnsembleVectorList = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetDeltaEnsembleVectorListResponse_api, - GetDeltaEnsembleVectorListError_api, + GetDeltaEnsembleVectorListResponse, + GetDeltaEnsembleVectorListError, ThrowOnError >({ ...options, @@ -295,11 +294,11 @@ export const getDeltaEnsembleVectorList = * Get vector data per realization */ export const getRealizationsVectorData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetRealizationsVectorDataResponse_api, - GetRealizationsVectorDataError_api, + GetRealizationsVectorDataResponse, + GetRealizationsVectorDataError, ThrowOnError >({ ...options, @@ -316,11 +315,11 @@ export const getRealizationsVectorData = ( * delta_ensemble = comparison_ensemble - reference_ensemble */ export const getDeltaEnsembleRealizationsVectorData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetDeltaEnsembleRealizationsVectorDataResponse_api, - GetDeltaEnsembleRealizationsVectorDataError_api, + GetDeltaEnsembleRealizationsVectorDataResponse, + GetDeltaEnsembleRealizationsVectorDataError, ThrowOnError >({ ...options, @@ -338,9 +337,9 @@ export const getDeltaEnsembleRealizationsVectorData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/timeseries/timestamps_list/", }); @@ -350,9 +349,9 @@ export const getTimestampsList = ( * Get Historical Vector Data */ export const getHistoricalVectorData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get( + return (options?.client ?? client).get( { ...options, url: "/timeseries/historical_vector_data/", @@ -365,11 +364,11 @@ export const getHistoricalVectorData = ( * Get statistical vector data for an ensemble */ export const getStatisticalVectorData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetStatisticalVectorDataResponse_api, - GetStatisticalVectorDataError_api, + GetStatisticalVectorDataResponse, + GetStatisticalVectorDataError, ThrowOnError >({ ...options, @@ -386,11 +385,11 @@ export const getStatisticalVectorData = ( * delta_ensemble = comparison_ensemble - reference_ensemble */ export const getDeltaEnsembleStatisticalVectorData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetDeltaEnsembleStatisticalVectorDataResponse_api, - GetDeltaEnsembleStatisticalVectorDataError_api, + GetDeltaEnsembleStatisticalVectorDataResponse, + GetDeltaEnsembleStatisticalVectorDataError, ThrowOnError >({ ...options, @@ -403,11 +402,11 @@ export const getDeltaEnsembleStatisticalVectorData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetStatisticalVectorDataPerSensitivityResponse_api, - GetStatisticalVectorDataPerSensitivityError_api, + GetStatisticalVectorDataPerSensitivityResponse, + GetStatisticalVectorDataPerSensitivityError, ThrowOnError >({ ...options, @@ -419,11 +418,11 @@ export const getStatisticalVectorDataPerSensitivity = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetRealizationVectorAtTimestampResponse_api, - GetRealizationVectorAtTimestampError_api, + GetRealizationVectorAtTimestampResponse, + GetRealizationVectorAtTimestampError, ThrowOnError >({ ...options, @@ -436,9 +435,9 @@ export const getRealizationVectorAtTimestamp = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/inplace_volumetrics/table_definitions/", }); @@ -452,11 +451,11 @@ export const getTableDefinitions = ( * As the endpoint is post, the identifiers with values object is kept for convenience. */ export const postGetAggregatedPerRealizationTableData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).post< - PostGetAggregatedPerRealizationTableDataResponse_api, - PostGetAggregatedPerRealizationTableDataError_api, + PostGetAggregatedPerRealizationTableDataResponse, + PostGetAggregatedPerRealizationTableDataError, ThrowOnError >({ ...options, @@ -476,11 +475,11 @@ export const postGetAggregatedPerRealizationTableData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).post< - PostGetAggregatedStatisticalTableDataResponse_api, - PostGetAggregatedStatisticalTableDataError_api, + PostGetAggregatedStatisticalTableDataResponse, + PostGetAggregatedStatisticalTableDataError, ThrowOnError >({ ...options, @@ -497,11 +496,11 @@ export const postGetAggregatedStatisticalTableData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetRealizationSurfacesMetadataResponse_api, - GetRealizationSurfacesMetadataError_api, + GetRealizationSurfacesMetadataResponse, + GetRealizationSurfacesMetadataError, ThrowOnError >({ ...options, @@ -514,11 +513,11 @@ export const getRealizationSurfacesMetadata = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetObservedSurfacesMetadataResponse_api, - GetObservedSurfacesMetadataError_api, + GetObservedSurfacesMetadataResponse, + GetObservedSurfacesMetadataError, ThrowOnError >({ ...options, @@ -551,9 +550,9 @@ export const getObservedSurfacesMetadata = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/surface/surface_data", }); @@ -567,11 +566,11 @@ export const getSurfaceData = ( * and cumulative lengths, the accumulated length at each z-point in the array. */ export const postGetSurfaceIntersection = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).post< - PostGetSurfaceIntersectionResponse_api, - PostGetSurfaceIntersectionError_api, + PostGetSurfaceIntersectionResponse, + PostGetSurfaceIntersectionError, ThrowOnError >({ ...options, @@ -587,11 +586,11 @@ export const postGetSurfaceIntersection = * Post Get Sample Surface In Points */ export const postGetSampleSurfaceInPoints = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).post< - PostGetSampleSurfaceInPointsResponse_api, - PostGetSampleSurfaceInPointsError_api, + PostGetSampleSurfaceInPointsResponse, + PostGetSampleSurfaceInPointsError, ThrowOnError >({ ...options, @@ -607,9 +606,9 @@ export const postGetSampleSurfaceInPoints = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/surface/delta_surface_data", }); @@ -619,9 +618,9 @@ export const getDeltaSurfaceData = ( * Get Misfit Surface Data */ export const getMisfitSurfaceData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/surface/misfit_surface_data", }); @@ -631,11 +630,11 @@ export const getMisfitSurfaceData = ( * Get Wellbore Stratigraphic Columns */ export const getWellboreStratigraphicColumns = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellboreStratigraphicColumnsResponse_api, - GetWellboreStratigraphicColumnsError_api, + GetWellboreStratigraphicColumnsResponse, + GetWellboreStratigraphicColumnsError, ThrowOnError >({ ...options, @@ -647,9 +646,9 @@ export const getWellboreStratigraphicColumns = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/surface/stratigraphic_units", }); @@ -660,11 +659,11 @@ export const getStratigraphicUnits = ( * Retrieve parameter names and description for an ensemble */ export const getParameterNamesAndDescription = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetParameterNamesAndDescriptionResponse_api, - GetParameterNamesAndDescriptionError_api, + GetParameterNamesAndDescriptionResponse, + GetParameterNamesAndDescriptionError, ThrowOnError >({ ...options, @@ -677,9 +676,9 @@ export const getParameterNamesAndDescription = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/parameters/parameter/", }); @@ -689,9 +688,9 @@ export const getParameter = ( * Get Parameters */ export const getParameters = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/parameters/parameters/", }); @@ -702,9 +701,9 @@ export const getParameters = ( * Check if a given Sumo ensemble is a sensitivity run */ export const getIsSensitivityRun = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/parameters/is_sensitivity_run/", }); @@ -715,9 +714,9 @@ export const getIsSensitivityRun = ( * Get sensitivities in a given Sumo ensemble */ export const getSensitivities = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/parameters/sensitivities/", }); @@ -728,9 +727,9 @@ export const getSensitivities = ( * Get metadata for all 3D grid models, including bbox, dimensions and properties */ export const getGridModelsInfo = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/grid3d/grid_models_info/", }); @@ -741,9 +740,9 @@ export const getGridModelsInfo = ( * Get a grid */ export const getGridSurface = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/grid3d/grid_surface", }); @@ -754,9 +753,9 @@ export const getGridSurface = ( * Get a grid parameter */ export const getGridParameter = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/grid3d/grid_parameter", }); @@ -766,11 +765,11 @@ export const getGridParameter = ( * Post Get Polyline Intersection */ export const postGetPolylineIntersection = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).post< - PostGetPolylineIntersectionResponse_api, - PostGetPolylineIntersectionError_api, + PostGetPolylineIntersectionResponse, + PostGetPolylineIntersectionError, ThrowOnError >({ ...options, @@ -786,11 +785,11 @@ export const postGetPolylineIntersection = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetRealizationFlowNetworkResponse_api, - GetRealizationFlowNetworkError_api, + GetRealizationFlowNetworkResponse, + GetRealizationFlowNetworkError, ThrowOnError >({ ...options, @@ -803,9 +802,9 @@ export const getRealizationFlowNetwork = ( * Get pvt table data for a given Sumo ensemble and realization */ export const getTableData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/pvt/table_data/", }); @@ -815,9 +814,9 @@ export const getTableData = ( * Get Well Completions Data */ export const getWellCompletionsData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/well_completions/well_completions_data/", }); @@ -828,11 +827,11 @@ export const getWellCompletionsData = ( * Get wellbore headers for all wells in the field */ export const getDrilledWellboreHeaders = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetDrilledWellboreHeadersResponse_api, - GetDrilledWellboreHeadersError_api, + GetDrilledWellboreHeadersResponse, + GetDrilledWellboreHeadersError, ThrowOnError >({ ...options, @@ -845,9 +844,9 @@ export const getDrilledWellboreHeaders = ( * Get well trajectories for field */ export const getWellTrajectories = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/well/well_trajectories/", }); @@ -858,11 +857,11 @@ export const getWellTrajectories = ( * Get wellbore pick identifiers for field and stratigraphic column */ export const getWellborePickIdentifiers = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellborePickIdentifiersResponse_api, - GetWellborePickIdentifiersError_api, + GetWellborePickIdentifiersResponse, + GetWellborePickIdentifiersError, ThrowOnError >({ ...options, @@ -875,11 +874,11 @@ export const getWellborePickIdentifiers = * Get wellbore picks for field and pick identifier */ export const getWellborePicksForPickIdentifier = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellborePicksForPickIdentifierResponse_api, - GetWellborePicksForPickIdentifierError_api, + GetWellborePicksForPickIdentifierResponse, + GetWellborePicksForPickIdentifierError, ThrowOnError >({ ...options, @@ -892,11 +891,11 @@ export const getWellborePicksForPickIdentifier = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellborePicksForWellboreResponse_api, - GetWellborePicksForWellboreError_api, + GetWellborePicksForWellboreResponse, + GetWellborePicksForWellboreError, ThrowOnError >({ ...options, @@ -908,11 +907,11 @@ export const getWellborePicksForWellbore = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellborePicksInStratColumnResponse_api, - GetWellborePicksInStratColumnError_api, + GetWellborePicksInStratColumnResponse, + GetWellborePicksInStratColumnError, ThrowOnError >({ ...options, @@ -925,9 +924,9 @@ export const getWellborePicksInStratColumn = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/well/wellbore_completions/", }); @@ -938,9 +937,9 @@ export const getWellboreCompletions = ( * Get well bore casings for a single well bore */ export const getWellboreCasings = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/well/wellbore_casings/", }); @@ -951,9 +950,9 @@ export const getWellboreCasings = ( * Get well bore casing for a single well bore */ export const getWellborePerforations = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get( + return (options?.client ?? client).get( { ...options, url: "/well/wellbore_perforations/", @@ -967,11 +966,11 @@ export const getWellborePerforations = ( * Logs are available from multiple sources, which can be specificed by the "sources" parameter. */ export const getWellboreLogCurveHeaders = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellboreLogCurveHeadersResponse_api, - GetWellboreLogCurveHeadersError_api, + GetWellboreLogCurveHeadersResponse, + GetWellboreLogCurveHeadersError, ThrowOnError >({ ...options, @@ -984,9 +983,9 @@ export const getWellboreLogCurveHeaders = * Get log curve data */ export const getLogCurveData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/well/log_curve_data/", }); @@ -997,9 +996,9 @@ export const getLogCurveData = ( * Get a list of seismic cube meta. */ export const getSeismicCubeMetaList = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/seismic/seismic_cube_meta_list/", }); @@ -1010,9 +1009,9 @@ export const getSeismicCubeMetaList = ( * Get a seismic inline from a seismic cube. */ export const getInlineSlice = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/seismic/get_inline_slice/", }); @@ -1023,9 +1022,9 @@ export const getInlineSlice = ( * Get a seismic crossline from a seismic cube. */ export const getCrosslineSlice = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/seismic/get_crossline_slice/", }); @@ -1036,9 +1035,9 @@ export const getCrosslineSlice = ( * Get a seismic depth slice from a seismic cube. */ export const getDepthSlice = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/seismic/get_depth_slice/", }); @@ -1055,9 +1054,9 @@ export const getDepthSlice = ( * A SeismicFenceData object with fence traces in encoded 1D array, metadata for trace array decoding and fence min/max depth. */ export const postGetSeismicFence = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).post({ + return (options?.client ?? client).post({ ...options, headers: { "Content-Type": "application/json", @@ -1072,9 +1071,9 @@ export const postGetSeismicFence = ( * Get a directory of polygons in a Sumo ensemble */ export const getPolygonsDirectory = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/polygons/polygons_directory/", }); @@ -1084,9 +1083,9 @@ export const getPolygonsDirectory = ( * Get Polygons Data */ export const getPolygonsData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/polygons/polygons_data/", }); @@ -1097,9 +1096,9 @@ export const getPolygonsData = ( * Get username, display name and avatar from Microsoft Graph API for a given user id */ export const getUserPhoto = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/graph/user_photo/", }); @@ -1110,9 +1109,9 @@ export const getUserPhoto = ( * Retrieve all observations found in sumo case */ export const getObservations = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/observations/observations/", }); @@ -1122,9 +1121,9 @@ export const getObservations = ( * Get Table Definition */ export const getTableDefinition = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/rft/table_definition", }); @@ -1134,9 +1133,9 @@ export const getTableDefinition = ( * Get Realization Data */ export const getRealizationData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/rft/realization_data", }); @@ -1146,9 +1145,9 @@ export const getRealizationData = ( * Get Vfp Table Names */ export const getVfpTableNames = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/vfp/vfp_table_names/", }); @@ -1157,8 +1156,8 @@ export const getVfpTableNames = ( /** * Get Vfp Table */ -export const getVfpTable = (options: Options) => { - return (options?.client ?? client).get({ +export const getVfpTable = (options: Options) => { + return (options?.client ?? client).get({ ...options, url: "/vfp/vfp_table/", }); @@ -1167,8 +1166,8 @@ export const getVfpTable = (options: Optio /** * Login Route */ -export const loginRoute = (options?: Options) => { - return (options?.client ?? client).get({ +export const loginRoute = (options?: Options) => { + return (options?.client ?? client).get({ ...options, url: "/login", }); @@ -1178,7 +1177,7 @@ export const loginRoute = (options?: Optio * Authorized Callback Route */ export const authorizedCallbackRoute = ( - options?: Options, + options?: Options, ) => { return (options?.client ?? client).get({ ...options, @@ -1189,8 +1188,8 @@ export const authorizedCallbackRoute = ( /** * Get Alive */ -export const getAlive = (options?: Options) => { - return (options?.client ?? client).get({ +export const getAlive = (options?: Options) => { + return (options?.client ?? client).get({ ...options, url: "/alive", }); @@ -1200,9 +1199,9 @@ export const getAlive = (options?: Options * Get Alive Protected */ export const getAliveProtected = ( - options?: Options, + options?: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/alive_protected", }); @@ -1211,8 +1210,8 @@ export const getAliveProtected = ( /** * Post Logout */ -export const postLogout = (options?: Options) => { - return (options?.client ?? client).post({ +export const postLogout = (options?: Options) => { + return (options?.client ?? client).post({ ...options, url: "/logout", }); @@ -1222,9 +1221,9 @@ export const postLogout = (options?: Optio * Get Logged In User */ export const getLoggedInUser = ( - options?: Options, + options?: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/logged_in_user", }); @@ -1233,8 +1232,8 @@ export const getLoggedInUser = ( /** * Root */ -export const root = (options?: Options) => { - return (options?.client ?? client).get({ +export const root = (options?: Options) => { + return (options?.client ?? client).get({ ...options, url: "/", }); diff --git a/frontend/src/api/autogen/types.gen.ts b/frontend/src/api/autogen/types.gen.ts index fe29e8548..76b11b6be 100644 --- a/frontend/src/api/autogen/types.gen.ts +++ b/frontend/src/api/autogen/types.gen.ts @@ -1,6 +1,6 @@ // This file is auto-generated by @hey-api/openapi-ts -export enum Alq_api { +export enum Alq { GRAT = "GRAT", IGLR = "IGLR", TGLR = "TGLR", @@ -12,47 +12,47 @@ export enum Alq_api { "''" = "''", } -export type B64FloatArray_api = { +export type B64FloatArray = { element_type: "float32" | "float64"; data_b64str: string; }; -export type B64UintArray_api = { +export type B64UintArray = { element_type: "uint8" | "uint16" | "uint32" | "uint64"; data_b64str: string; }; -export type BodyPostGetAggregatedPerRealizationTableData_api = { +export type BodyPostGetAggregatedPerRealizationTableData = { /** * Selected identifiers and wanted values */ - identifiers_with_values: Array; + identifiers_with_values: Array; }; -export type BodyPostGetAggregatedStatisticalTableData_api = { +export type BodyPostGetAggregatedStatisticalTableData = { /** * Selected identifiers and wanted values */ - identifiers_with_values: Array; + identifiers_with_values: Array; }; -export type BodyPostGetPolylineIntersection_api = { +export type BodyPostGetPolylineIntersection = { polyline_utm_xy: Array; }; -export type BodyPostGetSampleSurfaceInPoints_api = { - sample_points: PointSetXy_api; +export type BodyPostGetSampleSurfaceInPoints = { + sample_points: PointSetXy; }; -export type BodyPostGetSeismicFence_api = { - polyline: SeismicFencePolyline_api; +export type BodyPostGetSeismicFence = { + polyline: SeismicFencePolyline; }; -export type BodyPostGetSurfaceIntersection_api = { - cumulative_length_polyline: SurfaceIntersectionCumulativeLengthPolyline_api; +export type BodyPostGetSurfaceIntersection = { + cumulative_length_polyline: SurfaceIntersectionCumulativeLengthPolyline; }; -export type BoundingBox2D_api = { +export type BoundingBox2D = { min_x: number; min_y: number; max_x: number; @@ -62,7 +62,7 @@ export type BoundingBox2D_api = { /** * Bounding box for a 3D grid geometry */ -export type BoundingBox3D_api = { +export type BoundingBox3D = { xmin: number; ymin: number; zmin: number; @@ -71,14 +71,14 @@ export type BoundingBox3D_api = { zmax: number; }; -export type CaseInfo_api = { +export type CaseInfo = { uuid: string; name: string; status: string; user: string; }; -export type Completions_api = { +export type Completions = { sortedCompletionDateIndices: Array; open: Array; shut: Array; @@ -87,17 +87,17 @@ export type Completions_api = { khMax: Array; }; -export type DatedFlowNetwork_api = { +export type DatedFlowNetwork = { dates: Array; - network: NetworkNode_api; + network: NetworkNode; }; -export type DerivedVectorInfo_api = { - type: DerivedVectorType_api; +export type DerivedVectorInfo = { + type: DerivedVectorType; sourceVector: string; }; -export enum DerivedVectorType_api { +export enum DerivedVectorType { PER_DAY = "PER_DAY", PER_INTVL = "PER_INTVL", } @@ -105,13 +105,13 @@ export enum DerivedVectorType_api { /** * Holds information that describes how a discrete curve value should be presented to the user. */ -export type DiscreteValueMetadata_api = { +export type DiscreteValueMetadata = { code: number; identifier: string; rgbColor: [number, number, number]; }; -export type EnsembleDetails_api = { +export type EnsembleDetails = { name: string; field_identifier: string; case_name: string; @@ -120,7 +120,7 @@ export type EnsembleDetails_api = { stratigraphic_column_identifier: string; }; -export type EnsembleInfo_api = { +export type EnsembleInfo = { name: string; realization_count: number; }; @@ -128,80 +128,80 @@ export type EnsembleInfo_api = { /** * Description/data for a single parameter in an ensemble */ -export type EnsembleParameter_api = { +export type EnsembleParameter = { name: string; is_logarithmic: boolean; is_discrete: boolean; is_constant: boolean; - group_name?: string | null; - descriptive_name?: string | null; + group_name: string | null; + descriptive_name: string | null; realizations: Array; values: Array | Array | Array; }; -export type EnsembleParameterDescription_api = { +export type EnsembleParameterDescription = { name: string; - group_name?: string | null; - descriptive_name?: string | null; + group_name: string | null; + descriptive_name: string | null; is_discrete: boolean; }; /** * A generic type for a scalar response from each of the members of the ensemble. */ -export type EnsembleScalarResponse_api = { +export type EnsembleScalarResponse = { realizations: Array; values: Array; - name?: string | null; - unit?: string | null; + name: string | null; + unit: string | null; }; /** * Description/data for a single sensitivity in an ensemble */ -export type EnsembleSensitivity_api = { +export type EnsembleSensitivity = { name: string; - type: SensitivityType_api; - cases: Array; + type: SensitivityType; + cases: Array; }; /** * Description/data for a single sensitivity case in an ensemble */ -export type EnsembleSensitivityCase_api = { +export type EnsembleSensitivityCase = { name: string; realizations: Array; }; -export type FenceMeshSection_api = { - vertices_uz_b64arr: B64FloatArray_api; - poly_indices_b64arr: B64UintArray_api; - vertices_per_poly_b64arr: B64UintArray_api; - poly_source_cell_indices_b64arr: B64UintArray_api; - poly_props_b64arr: B64FloatArray_api; +export type FenceMeshSection = { + vertices_uz_b64arr: B64FloatArray; + poly_indices_b64arr: B64UintArray; + vertices_per_poly_b64arr: B64UintArray; + poly_source_cell_indices_b64arr: B64UintArray; + poly_props_b64arr: B64FloatArray; start_utm_x: number; start_utm_y: number; end_utm_x: number; end_utm_y: number; }; -export type FieldInfo_api = { +export type FieldInfo = { field_identifier: string; }; -export type FlowNetworkData_api = { - edgeMetadataList: Array; - nodeMetadataList: Array; - datedNetworks: Array; +export type FlowNetworkData = { + edgeMetadataList: Array; + nodeMetadataList: Array; + datedNetworks: Array; }; -export type FlowNetworkMetadata_api = { +export type FlowNetworkMetadata = { key: string; label: string; - unit?: string | null; + unit: string | null; }; -export enum FlowRateType_api { +export enum FlowRateType { OIL = "OIL", LIQ = "LIQ", GAS = "GAS", @@ -210,13 +210,13 @@ export enum FlowRateType_api { WAT = "WAT", } -export enum FluidZone_api { +export enum FluidZone { OIL = "Oil", GAS = "Gas", WATER = "Water", } -export enum Frequency_api { +export enum Frequency { DAILY = "DAILY", WEEKLY = "WEEKLY", MONTHLY = "MONTHLY", @@ -224,31 +224,31 @@ export enum Frequency_api { YEARLY = "YEARLY", } -export enum Gfr_api { +export enum Gfr { GOR = "GOR", GLR = "GLR", OGR = "OGR", MMW = "MMW", } -export type GraphUserPhoto_api = { - avatar_b64str?: string | null; +export type GraphUserPhoto = { + avatar_b64str: string | null; }; /** * Specification of a 3D grid dimensions */ -export type Grid3dDimensions_api = { +export type Grid3dDimensions = { i_count: number; j_count: number; k_count: number; - subgrids: Array; + subgrids: Array; }; -export type Grid3dGeometry_api = { - polys_b64arr: B64UintArray_api; - points_b64arr: B64FloatArray_api; - poly_source_cell_indices_b64arr: B64UintArray_api; +export type Grid3dGeometry = { + polys_b64arr: B64UintArray; + points_b64arr: B64FloatArray; + poly_source_cell_indices_b64arr: B64UintArray; origin_utm_x: number; origin_utm_y: number; xmin: number; @@ -262,15 +262,15 @@ export type Grid3dGeometry_api = { /** * Metadata for a 3D grid model, including its properties and geometry */ -export type Grid3dInfo_api = { +export type Grid3dInfo = { grid_name: string; - bbox: BoundingBox3D_api; - dimensions: Grid3dDimensions_api; - property_info_arr: Array; + bbox: BoundingBox3D; + dimensions: Grid3dDimensions; + property_info_arr: Array; }; -export type Grid3dMappedProperty_api = { - poly_props_b64arr: B64FloatArray_api; +export type Grid3dMappedProperty = { + poly_props_b64arr: B64FloatArray; min_grid_prop_value: number; max_grid_prop_value: number; }; @@ -278,28 +278,28 @@ export type Grid3dMappedProperty_api = { /** * Metadata for a 3D grid property */ -export type Grid3dPropertyInfo_api = { +export type Grid3dPropertyInfo = { property_name: string; - iso_date_or_interval?: string | null; + iso_date_or_interval: string | null; }; /** * Named subset of 3D grid layers (Zone) */ -export type Grid3dZone_api = { +export type Grid3dZone = { name: string; start_layer: number; end_layer: number; }; -export type GridDimensions_api = { +export type GridDimensions = { i_count: number; j_count: number; k_count: number; }; -export type HttpValidationError_api = { - detail?: Array; +export type HttpValidationError = { + detail?: Array; }; /** @@ -307,10 +307,10 @@ export type HttpValidationError_api = { * * Contains data for a single fluid zone, e.g. Oil, Gas, Water, or sum of fluid zones */ -export type InplaceStatisticalVolumetricTableData_api = { +export type InplaceStatisticalVolumetricTableData = { fluidSelectionName: string; - selectorColumns: Array; - resultColumnStatistics: Array; + selectorColumns: Array; + resultColumnStatistics: Array; }; /** @@ -318,14 +318,14 @@ export type InplaceStatisticalVolumetricTableData_api = { * * Fluid selection can be single fluid zones, e.g. Oil, Gas, Water, or sum of fluid zones - Oil + Gas + Water */ -export type InplaceStatisticalVolumetricTableDataPerFluidSelection_api = { - tableDataPerFluidSelection: Array; +export type InplaceStatisticalVolumetricTableDataPerFluidSelection = { + tableDataPerFluidSelection: Array; }; /** * Allowed volumetric response names */ -export enum InplaceVolumetricResultName_api { +export enum InplaceVolumetricResultName { BULK = "BULK", NET = "NET", PORO = "PORO", @@ -347,7 +347,7 @@ export enum InplaceVolumetricResultName_api { /** * Definition of possible statistics for a result column in an inplace volumetrics table */ -export enum InplaceVolumetricStatistic_api { +export enum InplaceVolumetricStatistic { MEAN = "mean", STDDEV = "stddev", MAX = "max", @@ -361,10 +361,10 @@ export enum InplaceVolumetricStatistic_api { * * Contains data for a single fluid zone, e.g. Oil, Gas, Water, or sum of fluid zones */ -export type InplaceVolumetricTableData_api = { +export type InplaceVolumetricTableData = { fluidSelectionName: string; - selectorColumns: Array; - resultColumns: Array; + selectorColumns: Array; + resultColumns: Array; }; /** @@ -372,11 +372,11 @@ export type InplaceVolumetricTableData_api = { * * Fluid selection can be single fluid zones, e.g. Oil, Gas, Water, or sum of fluid zones - Oil + Gas + Water */ -export type InplaceVolumetricTableDataPerFluidSelection_api = { - tableDataPerFluidSelection: Array; +export type InplaceVolumetricTableDataPerFluidSelection = { + tableDataPerFluidSelection: Array; }; -export enum InplaceVolumetricsIdentifier_api { +export enum InplaceVolumetricsIdentifier { ZONE = "ZONE", REGION = "REGION", FACIES = "FACIES", @@ -387,22 +387,22 @@ export enum InplaceVolumetricsIdentifier_api { * Unique values for an index column in a volumetric table * All values should ideally be strings, but it is common to see integers, especially for REGION */ -export type InplaceVolumetricsIdentifierWithValues_api = { - identifier: InplaceVolumetricsIdentifier_api; +export type InplaceVolumetricsIdentifierWithValues = { + identifier: InplaceVolumetricsIdentifier; values: Array; }; /** * Definition of a volumetric table */ -export type InplaceVolumetricsTableDefinition_api = { +export type InplaceVolumetricsTableDefinition = { tableName: string; - fluidZones: Array; - resultNames: Array; - identifiersWithValues: Array; + fluidZones: Array; + resultNames: Array; + identifiersWithValues: Array; }; -export type NetworkNode_api = { +export type NetworkNode = { node_type: "Group" | "Well"; node_label: string; edge_label: string; @@ -412,10 +412,10 @@ export type NetworkNode_api = { edge_data: { [key: string]: Array; }; - children: Array; + children: Array; }; -export enum NodeType_api { +export enum NodeType { PROD = "prod", INJ = "inj", OTHER = "other", @@ -424,17 +424,17 @@ export enum NodeType_api { /** * A collection of observations associated with a field/case/ensemble */ -export type Observations_api = { - summary?: Array; - rft?: Array; +export type Observations = { + summary: Array; + rft: Array; }; -export type PointSetXy_api = { +export type PointSetXy = { x_points: Array; y_points: Array; }; -export type PolygonData_api = { +export type PolygonData = { x_arr: Array; y_arr: Array; z_arr: Array; @@ -444,7 +444,7 @@ export type PolygonData_api = { /** * To be revisited later when the metadata is more mature. */ -export enum PolygonsAttributeType_api { +export enum PolygonsAttributeType { DEPTH = "depth", TIME = "time", PROPERTY = "property", @@ -459,24 +459,24 @@ export enum PolygonsAttributeType_api { NAMED_AREA = "named_area", } -export type PolygonsMeta_api = { +export type PolygonsMeta = { name: string; name_is_stratigraphic_offical: boolean; - stratigraphic_identifier?: string | null; - relative_stratigraphic_level?: number | null; - parent_stratigraphic_identifier?: string | null; + stratigraphic_identifier: string | null; + relative_stratigraphic_level: number | null; + parent_stratigraphic_identifier: string | null; attribute_name: string; - attribute_type: PolygonsAttributeType_api; + attribute_type: PolygonsAttributeType; }; -export type PolylineIntersection_api = { - fence_mesh_sections: Array; - grid_dimensions: GridDimensions_api; +export type PolylineIntersection = { + fence_mesh_sections: Array; + grid_dimensions: GridDimensions; min_grid_prop_value: number; max_grid_prop_value: number; }; -export type PvtData_api = { +export type PvtData = { name: string; phase: string; pvtnum: number; @@ -500,7 +500,7 @@ export type PvtData_api = { * - unique_values: List of unique values in the column * - indices: List of indices, in unique_values list, for each row in the table */ -export type RepeatedTableColumnData_api = { +export type RepeatedTableColumnData = { columnName: string; uniqueValues: Array; indices: Array; @@ -519,9 +519,9 @@ export type RepeatedTableColumnData_api = { * y (float): Y utm coordinate of the observation. * z (float): Z utm coordinate of the observation. */ -export type RftObservation_api = { +export type RftObservation = { value: number; - comment?: string | null; + comment: string | null; error: number; zone: string; md_msl: number; @@ -537,16 +537,16 @@ export type RftObservation_api = { * well (str): Unique well identifier * date (str): Observation date * comment (Optional[str]): An optional comment associated with the collection of observations. - * observations (List[RftObservation_api]): A list of RFT observations associated with this collection. + * observations (List[RftObservation]): A list of RFT observations associated with this collection. */ -export type RftObservations_api = { +export type RftObservations = { well: string; date: string; - comment?: string | null; - observations: Array; + comment: string | null; + observations: Array; }; -export type RftRealizationData_api = { +export type RftRealizationData = { well_name: string; realization: number; timestamp_utc_ms: number; @@ -554,12 +554,12 @@ export type RftRealizationData_api = { value_arr: Array; }; -export type RftTableDefinition_api = { +export type RftTableDefinition = { response_names: Array; - well_infos: Array; + well_infos: Array; }; -export type RftWellInfo_api = { +export type RftWellInfo = { well_name: string; timestamps_utc_ms: Array; }; @@ -567,14 +567,14 @@ export type RftWellInfo_api = { /** * Metadata for a seismic cube. */ -export type SeismicCubeMeta_api = { +export type SeismicCubeMeta = { seismicAttribute: string; unit: string; isoDateOrInterval: string; isObservation: boolean; isDepth: boolean; - bbox: BoundingBox3D_api; - spec: SeismicCubeSpec_api; + bbox: BoundingBox3D; + spec: SeismicCubeSpec; }; /** @@ -594,7 +594,7 @@ export type SeismicCubeMeta_api = { * - `zFlip`: {-1, 1} - The flip factor for the z-direction (1 if not flipped, -1 if flipped). * - `rotationDeg`: The rotation angle of the cube [deg]. */ -export type SeismicCubeSpec_api = { +export type SeismicCubeSpec = { numCols: number; numRows: number; numLayers: number; @@ -633,8 +633,8 @@ export type SeismicCubeSpec_api = { * See: * - VdsAxis: https://github.com/equinor/vds-slice/blob/ab6f39789bf3d3b59a8df14f1c4682d340dc0bf3/internal/core/core.go#L37-L55 */ -export type SeismicFenceData_api = { - fence_traces_b64arr: B64FloatArray_api; +export type SeismicFenceData = { + fence_traces_b64arr: B64FloatArray; num_traces: number; num_samples_per_trace: number; min_fence_depth: number; @@ -653,7 +653,7 @@ export type SeismicFenceData_api = { * - Consider points_xy: List[float] - i.e. array with [x1, y1, x2, y2, ..., xn, yn] instead of x_points and y_points arrays? * - Ensure equal length of x_points and y_points arrays? */ -export type SeismicFencePolyline_api = { +export type SeismicFencePolyline = { x_points: Array; y_points: Array; }; @@ -661,7 +661,7 @@ export type SeismicFencePolyline_api = { /** * Definition of a seismic slice from a seismic cube. This could be an inline, crossline, or depth slice. * u and v axes are the respective domain coordinate system axes, and the slice traces are the seismic data values. - * The SeismicCubeMeta_api specification object (not part of this schema) provides a transformation matrix for converting + * The SeismicCubeMeta specification object (not part of this schema) provides a transformation matrix for converting * the slice data from its own coordinate system (u,v) to the global coordinate system. * * `Properties:` @@ -680,8 +680,8 @@ export type SeismicFencePolyline_api = { * * Fence traces 1D array: [trace_1_sample_1, trace_1_sample_2, ..., trace_1_sample_n, ..., trace_m_sample_1, trace_m_sample_2, ..., trace_m_sample_n] */ -export type SeismicSliceData_api = { - slice_traces_b64arr: B64FloatArray_api; +export type SeismicSliceData = { + slice_traces_b64arr: B64FloatArray; bbox_utm: Array>; u_min: number; u_max: number; @@ -695,12 +695,12 @@ export type SeismicSliceData_api = { value_max: number; }; -export enum SensitivityType_api { +export enum SensitivityType { MONTECARLO = "montecarlo", SCENARIO = "scenario", } -export enum StatisticFunction_api { +export enum StatisticFunction { MEAN = "MEAN", MIN = "MIN", MAX = "MAX", @@ -709,15 +709,15 @@ export enum StatisticFunction_api { P50 = "P50", } -export type StatisticValueObject_api = { - statisticFunction: StatisticFunction_api; +export type StatisticValueObject = { + statisticFunction: StatisticFunction; values: Array; }; /** * Stratigraphic column from SMDA */ -export type StratigraphicColumn_api = { +export type StratigraphicColumn = { identifier: string; areaType: string; status: string; @@ -729,7 +729,7 @@ export type StratigraphicColumn_api = { * * Camel case attributes needed for esvIntersection component in front-end */ -export type StratigraphicUnit_api = { +export type StratigraphicUnit = { identifier: string; top: string; base: string; @@ -741,15 +741,15 @@ export type StratigraphicUnit_api = { colorR: number; colorG: number; colorB: number; - lithologyType?: number | number | string; + lithologyType: number | number | string; }; /** * A single observation of a summary vector at a specific date. */ -export type SummaryVectorDateObservation_api = { +export type SummaryVectorDateObservation = { date: string; - comment?: string | null; + comment: string | null; value: number; error: number; label: string; @@ -758,10 +758,10 @@ export type SummaryVectorDateObservation_api = { /** * A collection of observations of a summary vector. */ -export type SummaryVectorObservations_api = { +export type SummaryVectorObservations = { vector_name: string; - comment?: string | null; - observations: Array; + comment: string | null; + observations: Array; }; /** @@ -774,7 +774,7 @@ export type SummaryVectorObservations_api = { * * To be revisited later when the metadata is more mature. */ -export enum SurfaceAttributeType_api { +export enum SurfaceAttributeType { DEPTH = "depth", FACIES_THICKNESS = "facies_thickness", FLUID_CONTACT = "fluid_contact", @@ -789,25 +789,25 @@ export enum SurfaceAttributeType_api { UNKNOWN = "UNKNOWN", } -export type SurfaceDataFloat_api = { - format?: "float"; - surface_def: SurfaceDef_api; - transformed_bbox_utm: BoundingBox2D_api; +export type SurfaceDataFloat = { + format: "float"; + surface_def: SurfaceDef; + transformed_bbox_utm: BoundingBox2D; value_min: number; value_max: number; - values_b64arr: B64FloatArray_api; + values_b64arr: B64FloatArray; }; -export type SurfaceDataPng_api = { - format?: "png"; - surface_def: SurfaceDef_api; - transformed_bbox_utm: BoundingBox2D_api; +export type SurfaceDataPng = { + format: "png"; + surface_def: SurfaceDef; + transformed_bbox_utm: BoundingBox2D; value_min: number; value_max: number; png_image_base64: string; }; -export type SurfaceDef_api = { +export type SurfaceDef = { npoints_x: number; npoints_y: number; inc_x: number; @@ -833,7 +833,7 @@ export type SurfaceDef_api = { * * Note: Verify if cum_lengths is necessary with respect to xtgeo */ -export type SurfaceIntersectionCumulativeLengthPolyline_api = { +export type SurfaceIntersectionCumulativeLengthPolyline = { x_points: Array; y_points: Array; cum_lengths: Array; @@ -846,36 +846,36 @@ export type SurfaceIntersectionCumulativeLengthPolyline_api = { * z_points: Array of z-points (depth values) at the intersection points, i.e. depth value for each (x,y) point. * cum_lengths: Cumulative length values at the intersection points, i.e. accumulated length between each element in the z points. */ -export type SurfaceIntersectionData_api = { +export type SurfaceIntersectionData = { name: string; z_points: Array; cum_lengths: Array; }; -export type SurfaceMeta_api = { +export type SurfaceMeta = { name: string; name_is_stratigraphic_offical: boolean; attribute_name: string; - attribute_type: SurfaceAttributeType_api; - time_type: SurfaceTimeType_api; + attribute_type: SurfaceAttributeType; + time_type: SurfaceTimeType; is_observation: boolean; value_min: number | null; value_max: number | null; }; -export type SurfaceMetaSet_api = { - surfaces: Array; +export type SurfaceMetaSet = { + surfaces: Array; time_points_iso_str: Array; time_intervals_iso_str: Array; surface_names_in_strat_order: Array; }; -export type SurfaceRealizationSampleValues_api = { +export type SurfaceRealizationSampleValues = { realization: number; sampled_values: Array; }; -export enum SurfaceStatisticFunction_api { +export enum SurfaceStatisticFunction { MEAN = "MEAN", STD = "STD", MIN = "MIN", @@ -885,17 +885,15 @@ export enum SurfaceStatisticFunction_api { P50 = "P50", } -export enum SurfaceTimeType_api { +export enum SurfaceTimeType { NO_TIME = "NO_TIME", TIME_POINT = "TIME_POINT", INTERVAL = "INTERVAL", } -export enum Thp_api { - THP = "THP", -} +export type Thp = "THP"; -export enum TabType_api { +export enum TabType { BHP = "BHP", TEMP = "TEMP", } @@ -905,7 +903,7 @@ export enum TabType_api { * * Length of column values should be equal to the number of rows in the table */ -export type TableColumnData_api = { +export type TableColumnData = { columnName: string; columnValues: Array; }; @@ -915,14 +913,14 @@ export type TableColumnData_api = { * * Length of column values should be equal to the number of rows in the table */ -export type TableColumnStatisticalData_api = { +export type TableColumnStatisticalData = { columnName: string; statisticValues: { [key: string]: Array; }; }; -export enum UnitType_api { +export enum UnitType { METRIC = "METRIC", FIELD = "FIELD", LAB = "LAB", @@ -930,69 +928,69 @@ export enum UnitType_api { DEFAULT = "DEFAULT", } -export type UserInfo_api = { +export type UserInfo = { username: string; - display_name?: string | null; - avatar_b64str?: string | null; + display_name: string | null; + avatar_b64str: string | null; has_sumo_access: boolean; has_smda_access: boolean; }; -export type ValidationError_api = { +export type ValidationError = { loc: Array; msg: string; type: string; }; -export type VectorDescription_api = { +export type VectorDescription = { name: string; descriptiveName: string; hasHistorical: boolean; - derivedVectorInfo?: DerivedVectorInfo_api | null; + derivedVectorInfo: DerivedVectorInfo | null; }; -export type VectorHistoricalData_api = { +export type VectorHistoricalData = { timestampsUtcMs: Array; values: Array; unit: string; isRate: boolean; }; -export type VectorRealizationData_api = { +export type VectorRealizationData = { realization: number; timestampsUtcMs: Array; values: Array; unit: string; isRate: boolean; - derivedVectorInfo?: DerivedVectorInfo_api | null; + derivedVectorInfo: DerivedVectorInfo | null; }; -export type VectorStatisticData_api = { +export type VectorStatisticData = { realizations: Array; timestampsUtcMs: Array; - valueObjects: Array; + valueObjects: Array; unit: string; isRate: boolean; - derivedVectorInfo?: DerivedVectorInfo_api | null; + derivedVectorInfo: DerivedVectorInfo | null; }; -export type VectorStatisticSensitivityData_api = { +export type VectorStatisticSensitivityData = { realizations: Array; timestampsUtcMs: Array; - valueObjects: Array; + valueObjects: Array; unit: string; isRate: boolean; sensitivityName: string; sensitivityCase: string; }; -export type VfpInjTable_api = { - vfpType?: "INJ"; +export type VfpInjTable = { + vfpType: "INJ"; tableNumber: number; datum: number; - flowRateType: FlowRateType_api; - unitType: UnitType_api; - tabType: TabType_api; + flowRateType: FlowRateType; + unitType: UnitType; + tabType: TabType; thpValues: Array; flowRateValues: Array; bhpValues: Array; @@ -1001,23 +999,23 @@ export type VfpInjTable_api = { bhpUnit: string; }; -export type VfpProdTable_api = { - vfpType?: "PROD"; +export type VfpProdTable = { + vfpType: "PROD"; tableNumber: number; datum: number; - flowRateType: FlowRateType_api; - unitType: UnitType_api; - tabType: TabType_api; + flowRateType: FlowRateType; + unitType: UnitType; + tabType: TabType; thpValues: Array; flowRateValues: Array; bhpValues: Array; flowRateUnit: string; thpUnit: string; bhpUnit: string; - thpType: Thp_api; - wfrType: Wfr_api; - gfrType: Gfr_api; - alqType: Alq_api; + thpType: Thp; + wfrType: Wfr; + gfrType: Gfr; + alqType: Alq; wfrValues: Array; gfrValues: Array; alqValues: Array; @@ -1026,7 +1024,7 @@ export type VfpProdTable_api = { alqUnit: string; }; -export enum Wfr_api { +export enum Wfr { WOR = "WOR", WCT = "WCT", WGR = "WGR", @@ -1037,56 +1035,56 @@ export enum Wfr_api { /** * Type definition for well completions data */ -export type WellCompletionsData_api = { +export type WellCompletionsData = { version: string; - units: WellCompletionsUnits_api; - zones: Array; + units: WellCompletionsUnits; + zones: Array; sortedCompletionDates: Array; - wells: Array; + wells: Array; }; -export type WellCompletionsUnitInfo_api = { +export type WellCompletionsUnitInfo = { unit: string; decimalPlaces: number; }; -export type WellCompletionsUnits_api = { - kh: WellCompletionsUnitInfo_api; +export type WellCompletionsUnits = { + kh: WellCompletionsUnitInfo; }; -export type WellCompletionsWell_api = { +export type WellCompletionsWell = { name: string; attributes: { [key: string]: string | number | boolean; }; completions: { - [key: string]: Completions_api; + [key: string]: Completions; }; }; -export type WellCompletionsZone_api = { +export type WellCompletionsZone = { name: string; - subzones?: Array | null; + subzones: Array | null; }; -export enum WellLogCurveSourceEnum_api { +export enum WellLogCurveSourceEnum { SSDL_WELL_LOG = "ssdl.well_log", SMDA_GEOLOGY = "smda.geology", SMDA_STRATIGRAPHY = "smda.stratigraphy", } -export enum WellLogCurveTypeEnum_api { +export enum WellLogCurveTypeEnum { CONTINUOUS = "continuous", DISCRETE = "discrete", FLAG = "flag", } -export type WellboreCasing_api = { +export type WellboreCasing = { itemType: string; diameterNumeric: number; diameterInner: number; - description?: string | null; - remark?: string | null; + description: string | null; + remark: string | null; depthTopMd: number; depthBottomMd: number; totalDepthMd: number; @@ -1094,7 +1092,7 @@ export type WellboreCasing_api = { endDepth: number; }; -export type WellboreCompletion_api = { +export type WellboreCompletion = { mdTop: number; mdBottom: number; tvdTop: number | null; @@ -1104,7 +1102,7 @@ export type WellboreCompletion_api = { comment: string | null; }; -export type WellboreHeader_api = { +export type WellboreHeader = { wellboreUuid: string; uniqueWellboreIdentifier: string; wellUuid: string; @@ -1117,8 +1115,8 @@ export type WellboreHeader_api = { wellboreStatus: string; }; -export type WellboreLogCurveData_api = { - source: WellLogCurveSourceEnum_api; +export type WellboreLogCurveData = { + source: WellLogCurveSourceEnum; name: string; logName: string; indexMin: number; @@ -1132,18 +1130,18 @@ export type WellboreLogCurveData_api = { unit: string | null; curveUnitDesc: string | null; dataPoints: Array<[number, number | string | null]>; - discreteValueMetadata: Array | null; + discreteValueMetadata: Array | null; }; -export type WellboreLogCurveHeader_api = { - source: WellLogCurveSourceEnum_api; - curveType: WellLogCurveTypeEnum_api; +export type WellboreLogCurveHeader = { + source: WellLogCurveSourceEnum; + curveType: WellLogCurveTypeEnum; logName: string; curveName: string; curveUnit: string | null; }; -export type WellborePerforation_api = { +export type WellborePerforation = { mdTop: number; mdBottom: number; tvdTop: number; @@ -1157,7 +1155,7 @@ export type WellborePerforation_api = { * * Camel case attributes needed for esvIntersection component in front-end */ -export type WellborePick_api = { +export type WellborePick = { northing: number; easting: number; tvd: number; @@ -1167,13 +1165,13 @@ export type WellborePick_api = { uniqueWellboreIdentifier: string; wellboreUuid: string; pickIdentifier: string; - confidence?: string | null; + confidence: string | null; depthReferencePoint: string; mdUnit: string; interpreter: string | null; }; -export type WellboreTrajectory_api = { +export type WellboreTrajectory = { wellboreUuid: string; uniqueWellboreIdentifier: string; tvdMslArr: Array; @@ -1182,23 +1180,23 @@ export type WellboreTrajectory_api = { northingArr: Array; }; -export type GetFieldsData_api = { +export type GetFieldsData = { body?: never; path?: never; query?: never; url: "/fields"; }; -export type GetFieldsResponses_api = { +export type GetFieldsResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetFieldsResponse_api = GetFieldsResponses_api[keyof GetFieldsResponses_api]; +export type GetFieldsResponse = GetFieldsResponses[keyof GetFieldsResponses]; -export type GetCasesData_api = { +export type GetCasesData = { body?: never; path?: never; query: { @@ -1210,25 +1208,25 @@ export type GetCasesData_api = { url: "/cases"; }; -export type GetCasesErrors_api = { +export type GetCasesErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetCasesError_api = GetCasesErrors_api[keyof GetCasesErrors_api]; +export type GetCasesError = GetCasesErrors[keyof GetCasesErrors]; -export type GetCasesResponses_api = { +export type GetCasesResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetCasesResponse_api = GetCasesResponses_api[keyof GetCasesResponses_api]; +export type GetCasesResponse = GetCasesResponses[keyof GetCasesResponses]; -export type GetEnsemblesData_api = { +export type GetEnsemblesData = { body?: never; path: { /** @@ -1240,25 +1238,25 @@ export type GetEnsemblesData_api = { url: "/cases/{case_uuid}/ensembles"; }; -export type GetEnsemblesErrors_api = { +export type GetEnsemblesErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetEnsemblesError_api = GetEnsemblesErrors_api[keyof GetEnsemblesErrors_api]; +export type GetEnsemblesError = GetEnsemblesErrors[keyof GetEnsemblesErrors]; -export type GetEnsemblesResponses_api = { +export type GetEnsemblesResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetEnsemblesResponse_api = GetEnsemblesResponses_api[keyof GetEnsemblesResponses_api]; +export type GetEnsemblesResponse = GetEnsemblesResponses[keyof GetEnsemblesResponses]; -export type GetEnsembleDetailsData_api = { +export type GetEnsembleDetailsData = { body?: never; path: { /** @@ -1274,25 +1272,25 @@ export type GetEnsembleDetailsData_api = { url: "/cases/{case_uuid}/ensembles/{ensemble_name}"; }; -export type GetEnsembleDetailsErrors_api = { +export type GetEnsembleDetailsErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetEnsembleDetailsError_api = GetEnsembleDetailsErrors_api[keyof GetEnsembleDetailsErrors_api]; +export type GetEnsembleDetailsError = GetEnsembleDetailsErrors[keyof GetEnsembleDetailsErrors]; -export type GetEnsembleDetailsResponses_api = { +export type GetEnsembleDetailsResponses = { /** * Successful Response */ - 200: EnsembleDetails_api; + 200: EnsembleDetails; }; -export type GetEnsembleDetailsResponse_api = GetEnsembleDetailsResponses_api[keyof GetEnsembleDetailsResponses_api]; +export type GetEnsembleDetailsResponse = GetEnsembleDetailsResponses[keyof GetEnsembleDetailsResponses]; -export type GetVectorListData_api = { +export type GetVectorListData = { body?: never; path?: never; query: { @@ -1312,25 +1310,25 @@ export type GetVectorListData_api = { url: "/timeseries/vector_list/"; }; -export type GetVectorListErrors_api = { +export type GetVectorListErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetVectorListError_api = GetVectorListErrors_api[keyof GetVectorListErrors_api]; +export type GetVectorListError = GetVectorListErrors[keyof GetVectorListErrors]; -export type GetVectorListResponses_api = { +export type GetVectorListResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetVectorListResponse_api = GetVectorListResponses_api[keyof GetVectorListResponses_api]; +export type GetVectorListResponse = GetVectorListResponses[keyof GetVectorListResponses]; -export type GetDeltaEnsembleVectorListData_api = { +export type GetDeltaEnsembleVectorListData = { body?: never; path?: never; query: { @@ -1358,26 +1356,26 @@ export type GetDeltaEnsembleVectorListData_api = { url: "/timeseries/delta_ensemble_vector_list/"; }; -export type GetDeltaEnsembleVectorListErrors_api = { +export type GetDeltaEnsembleVectorListErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetDeltaEnsembleVectorListError_api = GetDeltaEnsembleVectorListErrors_api[keyof GetDeltaEnsembleVectorListErrors_api]; +export type GetDeltaEnsembleVectorListError = GetDeltaEnsembleVectorListErrors[keyof GetDeltaEnsembleVectorListErrors]; -export type GetDeltaEnsembleVectorListResponses_api = { +export type GetDeltaEnsembleVectorListResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetDeltaEnsembleVectorListResponse_api = - GetDeltaEnsembleVectorListResponses_api[keyof GetDeltaEnsembleVectorListResponses_api]; +export type GetDeltaEnsembleVectorListResponse = + GetDeltaEnsembleVectorListResponses[keyof GetDeltaEnsembleVectorListResponses]; -export type GetRealizationsVectorDataData_api = { +export type GetRealizationsVectorDataData = { body?: never; path?: never; query: { @@ -1396,7 +1394,7 @@ export type GetRealizationsVectorDataData_api = { /** * Resampling frequency. If not specified, raw data without resampling wil be returned. */ - resampling_frequency?: Frequency_api | null; + resampling_frequency?: Frequency | null; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1405,26 +1403,26 @@ export type GetRealizationsVectorDataData_api = { url: "/timeseries/realizations_vector_data/"; }; -export type GetRealizationsVectorDataErrors_api = { +export type GetRealizationsVectorDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetRealizationsVectorDataError_api = GetRealizationsVectorDataErrors_api[keyof GetRealizationsVectorDataErrors_api]; +export type GetRealizationsVectorDataError = GetRealizationsVectorDataErrors[keyof GetRealizationsVectorDataErrors]; -export type GetRealizationsVectorDataResponses_api = { +export type GetRealizationsVectorDataResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetRealizationsVectorDataResponse_api = - GetRealizationsVectorDataResponses_api[keyof GetRealizationsVectorDataResponses_api]; +export type GetRealizationsVectorDataResponse = + GetRealizationsVectorDataResponses[keyof GetRealizationsVectorDataResponses]; -export type GetDeltaEnsembleRealizationsVectorDataData_api = { +export type GetDeltaEnsembleRealizationsVectorDataData = { body?: never; path?: never; query: { @@ -1451,7 +1449,7 @@ export type GetDeltaEnsembleRealizationsVectorDataData_api = { /** * Resampling frequency */ - resampling_frequency: Frequency_api; + resampling_frequency: Frequency; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1460,27 +1458,27 @@ export type GetDeltaEnsembleRealizationsVectorDataData_api = { url: "/timeseries/delta_ensemble_realizations_vector_data/"; }; -export type GetDeltaEnsembleRealizationsVectorDataErrors_api = { +export type GetDeltaEnsembleRealizationsVectorDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetDeltaEnsembleRealizationsVectorDataError_api = - GetDeltaEnsembleRealizationsVectorDataErrors_api[keyof GetDeltaEnsembleRealizationsVectorDataErrors_api]; +export type GetDeltaEnsembleRealizationsVectorDataError = + GetDeltaEnsembleRealizationsVectorDataErrors[keyof GetDeltaEnsembleRealizationsVectorDataErrors]; -export type GetDeltaEnsembleRealizationsVectorDataResponses_api = { +export type GetDeltaEnsembleRealizationsVectorDataResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetDeltaEnsembleRealizationsVectorDataResponse_api = - GetDeltaEnsembleRealizationsVectorDataResponses_api[keyof GetDeltaEnsembleRealizationsVectorDataResponses_api]; +export type GetDeltaEnsembleRealizationsVectorDataResponse = + GetDeltaEnsembleRealizationsVectorDataResponses[keyof GetDeltaEnsembleRealizationsVectorDataResponses]; -export type GetTimestampsListData_api = { +export type GetTimestampsListData = { body?: never; path?: never; query: { @@ -1495,30 +1493,30 @@ export type GetTimestampsListData_api = { /** * Resampling frequency */ - resampling_frequency?: Frequency_api | null; + resampling_frequency?: Frequency | null; }; url: "/timeseries/timestamps_list/"; }; -export type GetTimestampsListErrors_api = { +export type GetTimestampsListErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetTimestampsListError_api = GetTimestampsListErrors_api[keyof GetTimestampsListErrors_api]; +export type GetTimestampsListError = GetTimestampsListErrors[keyof GetTimestampsListErrors]; -export type GetTimestampsListResponses_api = { +export type GetTimestampsListResponses = { /** * Successful Response */ 200: Array; }; -export type GetTimestampsListResponse_api = GetTimestampsListResponses_api[keyof GetTimestampsListResponses_api]; +export type GetTimestampsListResponse = GetTimestampsListResponses[keyof GetTimestampsListResponses]; -export type GetHistoricalVectorDataData_api = { +export type GetHistoricalVectorDataData = { body?: never; path?: never; query: { @@ -1537,30 +1535,30 @@ export type GetHistoricalVectorDataData_api = { /** * Resampling frequency */ - resampling_frequency?: Frequency_api | null; + resampling_frequency?: Frequency | null; }; url: "/timeseries/historical_vector_data/"; }; -export type GetHistoricalVectorDataErrors_api = { +export type GetHistoricalVectorDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetHistoricalVectorDataError_api = GetHistoricalVectorDataErrors_api[keyof GetHistoricalVectorDataErrors_api]; +export type GetHistoricalVectorDataError = GetHistoricalVectorDataErrors[keyof GetHistoricalVectorDataErrors]; -export type GetHistoricalVectorDataResponses_api = { +export type GetHistoricalVectorDataResponses = { /** * Successful Response */ - 200: VectorHistoricalData_api; + 200: VectorHistoricalData; }; -export type GetHistoricalVectorDataResponse_api = GetHistoricalVectorDataResponses_api[keyof GetHistoricalVectorDataResponses_api]; +export type GetHistoricalVectorDataResponse = GetHistoricalVectorDataResponses[keyof GetHistoricalVectorDataResponses]; -export type GetStatisticalVectorDataData_api = { +export type GetStatisticalVectorDataData = { body?: never; path?: never; query: { @@ -1579,11 +1577,11 @@ export type GetStatisticalVectorDataData_api = { /** * Resampling frequency */ - resampling_frequency: Frequency_api; + resampling_frequency: Frequency; /** * Optional list of statistics to calculate. If not specified, all statistics will be calculated. */ - statistic_functions?: Array | null; + statistic_functions?: Array | null; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1592,26 +1590,26 @@ export type GetStatisticalVectorDataData_api = { url: "/timeseries/statistical_vector_data/"; }; -export type GetStatisticalVectorDataErrors_api = { +export type GetStatisticalVectorDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetStatisticalVectorDataError_api = GetStatisticalVectorDataErrors_api[keyof GetStatisticalVectorDataErrors_api]; +export type GetStatisticalVectorDataError = GetStatisticalVectorDataErrors[keyof GetStatisticalVectorDataErrors]; -export type GetStatisticalVectorDataResponses_api = { +export type GetStatisticalVectorDataResponses = { /** * Successful Response */ - 200: VectorStatisticData_api; + 200: VectorStatisticData; }; -export type GetStatisticalVectorDataResponse_api = - GetStatisticalVectorDataResponses_api[keyof GetStatisticalVectorDataResponses_api]; +export type GetStatisticalVectorDataResponse = + GetStatisticalVectorDataResponses[keyof GetStatisticalVectorDataResponses]; -export type GetDeltaEnsembleStatisticalVectorDataData_api = { +export type GetDeltaEnsembleStatisticalVectorDataData = { body?: never; path?: never; query: { @@ -1638,11 +1636,11 @@ export type GetDeltaEnsembleStatisticalVectorDataData_api = { /** * Resampling frequency */ - resampling_frequency: Frequency_api; + resampling_frequency: Frequency; /** * Optional list of statistics to calculate. If not specified, all statistics will be calculated. */ - statistic_functions?: Array | null; + statistic_functions?: Array | null; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1651,27 +1649,27 @@ export type GetDeltaEnsembleStatisticalVectorDataData_api = { url: "/timeseries/delta_ensemble_statistical_vector_data/"; }; -export type GetDeltaEnsembleStatisticalVectorDataErrors_api = { +export type GetDeltaEnsembleStatisticalVectorDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetDeltaEnsembleStatisticalVectorDataError_api = - GetDeltaEnsembleStatisticalVectorDataErrors_api[keyof GetDeltaEnsembleStatisticalVectorDataErrors_api]; +export type GetDeltaEnsembleStatisticalVectorDataError = + GetDeltaEnsembleStatisticalVectorDataErrors[keyof GetDeltaEnsembleStatisticalVectorDataErrors]; -export type GetDeltaEnsembleStatisticalVectorDataResponses_api = { +export type GetDeltaEnsembleStatisticalVectorDataResponses = { /** * Successful Response */ - 200: VectorStatisticData_api; + 200: VectorStatisticData; }; -export type GetDeltaEnsembleStatisticalVectorDataResponse_api = - GetDeltaEnsembleStatisticalVectorDataResponses_api[keyof GetDeltaEnsembleStatisticalVectorDataResponses_api]; +export type GetDeltaEnsembleStatisticalVectorDataResponse = + GetDeltaEnsembleStatisticalVectorDataResponses[keyof GetDeltaEnsembleStatisticalVectorDataResponses]; -export type GetStatisticalVectorDataPerSensitivityData_api = { +export type GetStatisticalVectorDataPerSensitivityData = { body?: never; path?: never; query: { @@ -1690,11 +1688,11 @@ export type GetStatisticalVectorDataPerSensitivityData_api = { /** * Resampling frequency */ - resampling_frequency: Frequency_api; + resampling_frequency: Frequency; /** * Optional list of statistics to calculate. If not specified, all statistics will be calculated. */ - statistic_functions?: Array | null; + statistic_functions?: Array | null; /** * Optional list of realizations to include. If not specified, all realizations will be included. */ @@ -1703,27 +1701,27 @@ export type GetStatisticalVectorDataPerSensitivityData_api = { url: "/timeseries/statistical_vector_data_per_sensitivity/"; }; -export type GetStatisticalVectorDataPerSensitivityErrors_api = { +export type GetStatisticalVectorDataPerSensitivityErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetStatisticalVectorDataPerSensitivityError_api = - GetStatisticalVectorDataPerSensitivityErrors_api[keyof GetStatisticalVectorDataPerSensitivityErrors_api]; +export type GetStatisticalVectorDataPerSensitivityError = + GetStatisticalVectorDataPerSensitivityErrors[keyof GetStatisticalVectorDataPerSensitivityErrors]; -export type GetStatisticalVectorDataPerSensitivityResponses_api = { +export type GetStatisticalVectorDataPerSensitivityResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetStatisticalVectorDataPerSensitivityResponse_api = - GetStatisticalVectorDataPerSensitivityResponses_api[keyof GetStatisticalVectorDataPerSensitivityResponses_api]; +export type GetStatisticalVectorDataPerSensitivityResponse = + GetStatisticalVectorDataPerSensitivityResponses[keyof GetStatisticalVectorDataPerSensitivityResponses]; -export type GetRealizationVectorAtTimestampData_api = { +export type GetRealizationVectorAtTimestampData = { body?: never; path?: never; query: { @@ -1747,27 +1745,27 @@ export type GetRealizationVectorAtTimestampData_api = { url: "/timeseries/realization_vector_at_timestamp/"; }; -export type GetRealizationVectorAtTimestampErrors_api = { +export type GetRealizationVectorAtTimestampErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetRealizationVectorAtTimestampError_api = - GetRealizationVectorAtTimestampErrors_api[keyof GetRealizationVectorAtTimestampErrors_api]; +export type GetRealizationVectorAtTimestampError = + GetRealizationVectorAtTimestampErrors[keyof GetRealizationVectorAtTimestampErrors]; -export type GetRealizationVectorAtTimestampResponses_api = { +export type GetRealizationVectorAtTimestampResponses = { /** * Successful Response */ - 200: EnsembleScalarResponse_api; + 200: EnsembleScalarResponse; }; -export type GetRealizationVectorAtTimestampResponse_api = - GetRealizationVectorAtTimestampResponses_api[keyof GetRealizationVectorAtTimestampResponses_api]; +export type GetRealizationVectorAtTimestampResponse = + GetRealizationVectorAtTimestampResponses[keyof GetRealizationVectorAtTimestampResponses]; -export type GetTableDefinitionsData_api = { +export type GetTableDefinitionsData = { body?: never; path?: never; query: { @@ -1783,26 +1781,26 @@ export type GetTableDefinitionsData_api = { url: "/inplace_volumetrics/table_definitions/"; }; -export type GetTableDefinitionsErrors_api = { +export type GetTableDefinitionsErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetTableDefinitionsError_api = GetTableDefinitionsErrors_api[keyof GetTableDefinitionsErrors_api]; +export type GetTableDefinitionsError = GetTableDefinitionsErrors[keyof GetTableDefinitionsErrors]; -export type GetTableDefinitionsResponses_api = { +export type GetTableDefinitionsResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetTableDefinitionsResponse_api = GetTableDefinitionsResponses_api[keyof GetTableDefinitionsResponses_api]; +export type GetTableDefinitionsResponse = GetTableDefinitionsResponses[keyof GetTableDefinitionsResponses]; -export type PostGetAggregatedPerRealizationTableDataData_api = { - body: BodyPostGetAggregatedPerRealizationTableData_api; +export type PostGetAggregatedPerRealizationTableDataData = { + body: BodyPostGetAggregatedPerRealizationTableData; path?: never; query: { /** @@ -1824,7 +1822,7 @@ export type PostGetAggregatedPerRealizationTableDataData_api = { /** * The fluid zones to aggregate by */ - fluid_zones: Array; + fluid_zones: Array; /** * Whether to accumulate fluid zones */ @@ -1832,7 +1830,7 @@ export type PostGetAggregatedPerRealizationTableDataData_api = { /** * The identifiers to group table data by */ - group_by_identifiers?: Array | null; + group_by_identifiers?: Array | null; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1841,28 +1839,28 @@ export type PostGetAggregatedPerRealizationTableDataData_api = { url: "/inplace_volumetrics/get_aggregated_per_realization_table_data/"; }; -export type PostGetAggregatedPerRealizationTableDataErrors_api = { +export type PostGetAggregatedPerRealizationTableDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type PostGetAggregatedPerRealizationTableDataError_api = - PostGetAggregatedPerRealizationTableDataErrors_api[keyof PostGetAggregatedPerRealizationTableDataErrors_api]; +export type PostGetAggregatedPerRealizationTableDataError = + PostGetAggregatedPerRealizationTableDataErrors[keyof PostGetAggregatedPerRealizationTableDataErrors]; -export type PostGetAggregatedPerRealizationTableDataResponses_api = { +export type PostGetAggregatedPerRealizationTableDataResponses = { /** * Successful Response */ - 200: InplaceVolumetricTableDataPerFluidSelection_api; + 200: InplaceVolumetricTableDataPerFluidSelection; }; -export type PostGetAggregatedPerRealizationTableDataResponse_api = - PostGetAggregatedPerRealizationTableDataResponses_api[keyof PostGetAggregatedPerRealizationTableDataResponses_api]; +export type PostGetAggregatedPerRealizationTableDataResponse = + PostGetAggregatedPerRealizationTableDataResponses[keyof PostGetAggregatedPerRealizationTableDataResponses]; -export type PostGetAggregatedStatisticalTableDataData_api = { - body: BodyPostGetAggregatedStatisticalTableData_api; +export type PostGetAggregatedStatisticalTableDataData = { + body: BodyPostGetAggregatedStatisticalTableData; path?: never; query: { /** @@ -1884,7 +1882,7 @@ export type PostGetAggregatedStatisticalTableDataData_api = { /** * The fluid zones to aggregate by */ - fluid_zones: Array; + fluid_zones: Array; /** * Whether to accumulate fluid zones */ @@ -1892,7 +1890,7 @@ export type PostGetAggregatedStatisticalTableDataData_api = { /** * The identifiers to group table data by */ - group_by_identifiers?: Array | null; + group_by_identifiers?: Array | null; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1901,27 +1899,27 @@ export type PostGetAggregatedStatisticalTableDataData_api = { url: "/inplace_volumetrics/get_aggregated_statistical_table_data/"; }; -export type PostGetAggregatedStatisticalTableDataErrors_api = { +export type PostGetAggregatedStatisticalTableDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type PostGetAggregatedStatisticalTableDataError_api = - PostGetAggregatedStatisticalTableDataErrors_api[keyof PostGetAggregatedStatisticalTableDataErrors_api]; +export type PostGetAggregatedStatisticalTableDataError = + PostGetAggregatedStatisticalTableDataErrors[keyof PostGetAggregatedStatisticalTableDataErrors]; -export type PostGetAggregatedStatisticalTableDataResponses_api = { +export type PostGetAggregatedStatisticalTableDataResponses = { /** * Successful Response */ - 200: InplaceStatisticalVolumetricTableDataPerFluidSelection_api; + 200: InplaceStatisticalVolumetricTableDataPerFluidSelection; }; -export type PostGetAggregatedStatisticalTableDataResponse_api = - PostGetAggregatedStatisticalTableDataResponses_api[keyof PostGetAggregatedStatisticalTableDataResponses_api]; +export type PostGetAggregatedStatisticalTableDataResponse = + PostGetAggregatedStatisticalTableDataResponses[keyof PostGetAggregatedStatisticalTableDataResponses]; -export type GetRealizationSurfacesMetadataData_api = { +export type GetRealizationSurfacesMetadataData = { body?: never; path?: never; query: { @@ -1937,27 +1935,27 @@ export type GetRealizationSurfacesMetadataData_api = { url: "/surface/realization_surfaces_metadata/"; }; -export type GetRealizationSurfacesMetadataErrors_api = { +export type GetRealizationSurfacesMetadataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetRealizationSurfacesMetadataError_api = - GetRealizationSurfacesMetadataErrors_api[keyof GetRealizationSurfacesMetadataErrors_api]; +export type GetRealizationSurfacesMetadataError = + GetRealizationSurfacesMetadataErrors[keyof GetRealizationSurfacesMetadataErrors]; -export type GetRealizationSurfacesMetadataResponses_api = { +export type GetRealizationSurfacesMetadataResponses = { /** * Successful Response */ - 200: SurfaceMetaSet_api; + 200: SurfaceMetaSet; }; -export type GetRealizationSurfacesMetadataResponse_api = - GetRealizationSurfacesMetadataResponses_api[keyof GetRealizationSurfacesMetadataResponses_api]; +export type GetRealizationSurfacesMetadataResponse = + GetRealizationSurfacesMetadataResponses[keyof GetRealizationSurfacesMetadataResponses]; -export type GetObservedSurfacesMetadataData_api = { +export type GetObservedSurfacesMetadataData = { body?: never; path?: never; query: { @@ -1969,27 +1967,27 @@ export type GetObservedSurfacesMetadataData_api = { url: "/surface/observed_surfaces_metadata/"; }; -export type GetObservedSurfacesMetadataErrors_api = { +export type GetObservedSurfacesMetadataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetObservedSurfacesMetadataError_api = - GetObservedSurfacesMetadataErrors_api[keyof GetObservedSurfacesMetadataErrors_api]; +export type GetObservedSurfacesMetadataError = + GetObservedSurfacesMetadataErrors[keyof GetObservedSurfacesMetadataErrors]; -export type GetObservedSurfacesMetadataResponses_api = { +export type GetObservedSurfacesMetadataResponses = { /** * Successful Response */ - 200: SurfaceMetaSet_api; + 200: SurfaceMetaSet; }; -export type GetObservedSurfacesMetadataResponse_api = - GetObservedSurfacesMetadataResponses_api[keyof GetObservedSurfacesMetadataResponses_api]; +export type GetObservedSurfacesMetadataResponse = + GetObservedSurfacesMetadataResponses[keyof GetObservedSurfacesMetadataResponses]; -export type GetSurfaceDataData_api = { +export type GetSurfaceDataData = { body?: never; path?: never; query: { @@ -2002,33 +2000,33 @@ export type GetSurfaceDataData_api = { */ data_format?: "float" | "png"; /** - * Definition of the surface onto which the data should be resampled. *SurfaceDef_api* object properties encoded as a `KeyValStr` string. + * Definition of the surface onto which the data should be resampled. *SurfaceDef* object properties encoded as a `KeyValStr` string. */ resample_to_def_str?: string | null; }; url: "/surface/surface_data"; }; -export type GetSurfaceDataErrors_api = { +export type GetSurfaceDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetSurfaceDataError_api = GetSurfaceDataErrors_api[keyof GetSurfaceDataErrors_api]; +export type GetSurfaceDataError = GetSurfaceDataErrors[keyof GetSurfaceDataErrors]; -export type GetSurfaceDataResponses_api = { +export type GetSurfaceDataResponses = { /** * Successful Response */ - 200: SurfaceDataFloat_api | SurfaceDataPng_api; + 200: SurfaceDataFloat | SurfaceDataPng; }; -export type GetSurfaceDataResponse_api = GetSurfaceDataResponses_api[keyof GetSurfaceDataResponses_api]; +export type GetSurfaceDataResponse = GetSurfaceDataResponses[keyof GetSurfaceDataResponses]; -export type PostGetSurfaceIntersectionData_api = { - body: BodyPostGetSurfaceIntersection_api; +export type PostGetSurfaceIntersectionData = { + body: BodyPostGetSurfaceIntersection; path?: never; query: { /** @@ -2059,27 +2057,27 @@ export type PostGetSurfaceIntersectionData_api = { url: "/surface/get_surface_intersection"; }; -export type PostGetSurfaceIntersectionErrors_api = { +export type PostGetSurfaceIntersectionErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type PostGetSurfaceIntersectionError_api = PostGetSurfaceIntersectionErrors_api[keyof PostGetSurfaceIntersectionErrors_api]; +export type PostGetSurfaceIntersectionError = PostGetSurfaceIntersectionErrors[keyof PostGetSurfaceIntersectionErrors]; -export type PostGetSurfaceIntersectionResponses_api = { +export type PostGetSurfaceIntersectionResponses = { /** * Successful Response */ - 200: SurfaceIntersectionData_api; + 200: SurfaceIntersectionData; }; -export type PostGetSurfaceIntersectionResponse_api = - PostGetSurfaceIntersectionResponses_api[keyof PostGetSurfaceIntersectionResponses_api]; +export type PostGetSurfaceIntersectionResponse = + PostGetSurfaceIntersectionResponses[keyof PostGetSurfaceIntersectionResponses]; -export type PostGetSampleSurfaceInPointsData_api = { - body: BodyPostGetSampleSurfaceInPoints_api; +export type PostGetSampleSurfaceInPointsData = { + body: BodyPostGetSampleSurfaceInPoints; path?: never; query: { /** @@ -2106,27 +2104,27 @@ export type PostGetSampleSurfaceInPointsData_api = { url: "/surface/get_sample_surface_in_points"; }; -export type PostGetSampleSurfaceInPointsErrors_api = { +export type PostGetSampleSurfaceInPointsErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type PostGetSampleSurfaceInPointsError_api = - PostGetSampleSurfaceInPointsErrors_api[keyof PostGetSampleSurfaceInPointsErrors_api]; +export type PostGetSampleSurfaceInPointsError = + PostGetSampleSurfaceInPointsErrors[keyof PostGetSampleSurfaceInPointsErrors]; -export type PostGetSampleSurfaceInPointsResponses_api = { +export type PostGetSampleSurfaceInPointsResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type PostGetSampleSurfaceInPointsResponse_api = - PostGetSampleSurfaceInPointsResponses_api[keyof PostGetSampleSurfaceInPointsResponses_api]; +export type PostGetSampleSurfaceInPointsResponse = + PostGetSampleSurfaceInPointsResponses[keyof PostGetSampleSurfaceInPointsResponses]; -export type GetDeltaSurfaceDataData_api = { +export type GetDeltaSurfaceDataData = { body?: never; path?: never; query: { @@ -2143,32 +2141,32 @@ export type GetDeltaSurfaceDataData_api = { */ data_format?: "float" | "png"; /** - * Definition of the surface onto which the data should be resampled. *SurfaceDef_api* object properties encoded as a `KeyValStr` string. + * Definition of the surface onto which the data should be resampled. *SurfaceDef* object properties encoded as a `KeyValStr` string. */ resample_to_def_str?: string | null; }; url: "/surface/delta_surface_data"; }; -export type GetDeltaSurfaceDataErrors_api = { +export type GetDeltaSurfaceDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetDeltaSurfaceDataError_api = GetDeltaSurfaceDataErrors_api[keyof GetDeltaSurfaceDataErrors_api]; +export type GetDeltaSurfaceDataError = GetDeltaSurfaceDataErrors[keyof GetDeltaSurfaceDataErrors]; -export type GetDeltaSurfaceDataResponses_api = { +export type GetDeltaSurfaceDataResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetDeltaSurfaceDataResponse_api = GetDeltaSurfaceDataResponses_api[keyof GetDeltaSurfaceDataResponses_api]; +export type GetDeltaSurfaceDataResponse = GetDeltaSurfaceDataResponses[keyof GetDeltaSurfaceDataResponses]; -export type GetMisfitSurfaceDataData_api = { +export type GetMisfitSurfaceDataData = { body?: never; path?: never; query: { @@ -2183,7 +2181,7 @@ export type GetMisfitSurfaceDataData_api = { /** * Statistics to calculate */ - statistic_functions: Array; + statistic_functions: Array; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -2193,32 +2191,32 @@ export type GetMisfitSurfaceDataData_api = { */ data_format?: "float" | "png"; /** - * Definition of the surface onto which the data should be resampled. *SurfaceDef_api* object properties encoded as a `KeyValStr` string. + * Definition of the surface onto which the data should be resampled. *SurfaceDef* object properties encoded as a `KeyValStr` string. */ resample_to_def_str?: string | null; }; url: "/surface/misfit_surface_data"; }; -export type GetMisfitSurfaceDataErrors_api = { +export type GetMisfitSurfaceDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetMisfitSurfaceDataError_api = GetMisfitSurfaceDataErrors_api[keyof GetMisfitSurfaceDataErrors_api]; +export type GetMisfitSurfaceDataError = GetMisfitSurfaceDataErrors[keyof GetMisfitSurfaceDataErrors]; -export type GetMisfitSurfaceDataResponses_api = { +export type GetMisfitSurfaceDataResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetMisfitSurfaceDataResponse_api = GetMisfitSurfaceDataResponses_api[keyof GetMisfitSurfaceDataResponses_api]; +export type GetMisfitSurfaceDataResponse = GetMisfitSurfaceDataResponses[keyof GetMisfitSurfaceDataResponses]; -export type GetWellboreStratigraphicColumnsData_api = { +export type GetWellboreStratigraphicColumnsData = { body?: never; path?: never; query: { @@ -2230,27 +2228,27 @@ export type GetWellboreStratigraphicColumnsData_api = { url: "/surface/wellbore_stratigraphic_columns/"; }; -export type GetWellboreStratigraphicColumnsErrors_api = { +export type GetWellboreStratigraphicColumnsErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetWellboreStratigraphicColumnsError_api = - GetWellboreStratigraphicColumnsErrors_api[keyof GetWellboreStratigraphicColumnsErrors_api]; +export type GetWellboreStratigraphicColumnsError = + GetWellboreStratigraphicColumnsErrors[keyof GetWellboreStratigraphicColumnsErrors]; -export type GetWellboreStratigraphicColumnsResponses_api = { +export type GetWellboreStratigraphicColumnsResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellboreStratigraphicColumnsResponse_api = - GetWellboreStratigraphicColumnsResponses_api[keyof GetWellboreStratigraphicColumnsResponses_api]; +export type GetWellboreStratigraphicColumnsResponse = + GetWellboreStratigraphicColumnsResponses[keyof GetWellboreStratigraphicColumnsResponses]; -export type GetStratigraphicUnitsData_api = { +export type GetStratigraphicUnitsData = { body?: never; path?: never; query: { @@ -2262,25 +2260,25 @@ export type GetStratigraphicUnitsData_api = { url: "/surface/stratigraphic_units"; }; -export type GetStratigraphicUnitsErrors_api = { +export type GetStratigraphicUnitsErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetStratigraphicUnitsError_api = GetStratigraphicUnitsErrors_api[keyof GetStratigraphicUnitsErrors_api]; +export type GetStratigraphicUnitsError = GetStratigraphicUnitsErrors[keyof GetStratigraphicUnitsErrors]; -export type GetStratigraphicUnitsResponses_api = { +export type GetStratigraphicUnitsResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetStratigraphicUnitsResponse_api = GetStratigraphicUnitsResponses_api[keyof GetStratigraphicUnitsResponses_api]; +export type GetStratigraphicUnitsResponse = GetStratigraphicUnitsResponses[keyof GetStratigraphicUnitsResponses]; -export type GetParameterNamesAndDescriptionData_api = { +export type GetParameterNamesAndDescriptionData = { body?: never; path?: never; query: { @@ -2304,27 +2302,27 @@ export type GetParameterNamesAndDescriptionData_api = { url: "/parameters/parameter_names_and_description/"; }; -export type GetParameterNamesAndDescriptionErrors_api = { +export type GetParameterNamesAndDescriptionErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetParameterNamesAndDescriptionError_api = - GetParameterNamesAndDescriptionErrors_api[keyof GetParameterNamesAndDescriptionErrors_api]; +export type GetParameterNamesAndDescriptionError = + GetParameterNamesAndDescriptionErrors[keyof GetParameterNamesAndDescriptionErrors]; -export type GetParameterNamesAndDescriptionResponses_api = { +export type GetParameterNamesAndDescriptionResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetParameterNamesAndDescriptionResponse_api = - GetParameterNamesAndDescriptionResponses_api[keyof GetParameterNamesAndDescriptionResponses_api]; +export type GetParameterNamesAndDescriptionResponse = + GetParameterNamesAndDescriptionResponses[keyof GetParameterNamesAndDescriptionResponses]; -export type GetParameterData_api = { +export type GetParameterData = { body?: never; path?: never; query: { @@ -2344,25 +2342,25 @@ export type GetParameterData_api = { url: "/parameters/parameter/"; }; -export type GetParameterErrors_api = { +export type GetParameterErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetParameterError_api = GetParameterErrors_api[keyof GetParameterErrors_api]; +export type GetParameterError = GetParameterErrors[keyof GetParameterErrors]; -export type GetParameterResponses_api = { +export type GetParameterResponses = { /** * Successful Response */ - 200: EnsembleParameter_api | null; + 200: EnsembleParameter | null; }; -export type GetParameterResponse_api = GetParameterResponses_api[keyof GetParameterResponses_api]; +export type GetParameterResponse = GetParameterResponses[keyof GetParameterResponses]; -export type GetParametersData_api = { +export type GetParametersData = { body?: never; path?: never; query: { @@ -2378,25 +2376,25 @@ export type GetParametersData_api = { url: "/parameters/parameters/"; }; -export type GetParametersErrors_api = { +export type GetParametersErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetParametersError_api = GetParametersErrors_api[keyof GetParametersErrors_api]; +export type GetParametersError = GetParametersErrors[keyof GetParametersErrors]; -export type GetParametersResponses_api = { +export type GetParametersResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetParametersResponse_api = GetParametersResponses_api[keyof GetParametersResponses_api]; +export type GetParametersResponse = GetParametersResponses[keyof GetParametersResponses]; -export type GetIsSensitivityRunData_api = { +export type GetIsSensitivityRunData = { body?: never; path?: never; query: { @@ -2412,25 +2410,25 @@ export type GetIsSensitivityRunData_api = { url: "/parameters/is_sensitivity_run/"; }; -export type GetIsSensitivityRunErrors_api = { +export type GetIsSensitivityRunErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetIsSensitivityRunError_api = GetIsSensitivityRunErrors_api[keyof GetIsSensitivityRunErrors_api]; +export type GetIsSensitivityRunError = GetIsSensitivityRunErrors[keyof GetIsSensitivityRunErrors]; -export type GetIsSensitivityRunResponses_api = { +export type GetIsSensitivityRunResponses = { /** * Successful Response */ 200: boolean; }; -export type GetIsSensitivityRunResponse_api = GetIsSensitivityRunResponses_api[keyof GetIsSensitivityRunResponses_api]; +export type GetIsSensitivityRunResponse = GetIsSensitivityRunResponses[keyof GetIsSensitivityRunResponses]; -export type GetSensitivitiesData_api = { +export type GetSensitivitiesData = { body?: never; path?: never; query: { @@ -2446,25 +2444,25 @@ export type GetSensitivitiesData_api = { url: "/parameters/sensitivities/"; }; -export type GetSensitivitiesErrors_api = { +export type GetSensitivitiesErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetSensitivitiesError_api = GetSensitivitiesErrors_api[keyof GetSensitivitiesErrors_api]; +export type GetSensitivitiesError = GetSensitivitiesErrors[keyof GetSensitivitiesErrors]; -export type GetSensitivitiesResponses_api = { +export type GetSensitivitiesResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetSensitivitiesResponse_api = GetSensitivitiesResponses_api[keyof GetSensitivitiesResponses_api]; +export type GetSensitivitiesResponse = GetSensitivitiesResponses[keyof GetSensitivitiesResponses]; -export type GetGridModelsInfoData_api = { +export type GetGridModelsInfoData = { body?: never; path?: never; query: { @@ -2484,25 +2482,25 @@ export type GetGridModelsInfoData_api = { url: "/grid3d/grid_models_info/"; }; -export type GetGridModelsInfoErrors_api = { +export type GetGridModelsInfoErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetGridModelsInfoError_api = GetGridModelsInfoErrors_api[keyof GetGridModelsInfoErrors_api]; +export type GetGridModelsInfoError = GetGridModelsInfoErrors[keyof GetGridModelsInfoErrors]; -export type GetGridModelsInfoResponses_api = { +export type GetGridModelsInfoResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetGridModelsInfoResponse_api = GetGridModelsInfoResponses_api[keyof GetGridModelsInfoResponses_api]; +export type GetGridModelsInfoResponse = GetGridModelsInfoResponses[keyof GetGridModelsInfoResponses]; -export type GetGridSurfaceData_api = { +export type GetGridSurfaceData = { body?: never; path?: never; query: { @@ -2550,25 +2548,25 @@ export type GetGridSurfaceData_api = { url: "/grid3d/grid_surface"; }; -export type GetGridSurfaceErrors_api = { +export type GetGridSurfaceErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetGridSurfaceError_api = GetGridSurfaceErrors_api[keyof GetGridSurfaceErrors_api]; +export type GetGridSurfaceError = GetGridSurfaceErrors[keyof GetGridSurfaceErrors]; -export type GetGridSurfaceResponses_api = { +export type GetGridSurfaceResponses = { /** * Successful Response */ - 200: Grid3dGeometry_api; + 200: Grid3dGeometry; }; -export type GetGridSurfaceResponse_api = GetGridSurfaceResponses_api[keyof GetGridSurfaceResponses_api]; +export type GetGridSurfaceResponse = GetGridSurfaceResponses[keyof GetGridSurfaceResponses]; -export type GetGridParameterData_api = { +export type GetGridParameterData = { body?: never; path?: never; query: { @@ -2624,26 +2622,26 @@ export type GetGridParameterData_api = { url: "/grid3d/grid_parameter"; }; -export type GetGridParameterErrors_api = { +export type GetGridParameterErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetGridParameterError_api = GetGridParameterErrors_api[keyof GetGridParameterErrors_api]; +export type GetGridParameterError = GetGridParameterErrors[keyof GetGridParameterErrors]; -export type GetGridParameterResponses_api = { +export type GetGridParameterResponses = { /** * Successful Response */ - 200: Grid3dMappedProperty_api; + 200: Grid3dMappedProperty; }; -export type GetGridParameterResponse_api = GetGridParameterResponses_api[keyof GetGridParameterResponses_api]; +export type GetGridParameterResponse = GetGridParameterResponses[keyof GetGridParameterResponses]; -export type PostGetPolylineIntersectionData_api = { - body: BodyPostGetPolylineIntersection_api; +export type PostGetPolylineIntersectionData = { + body: BodyPostGetPolylineIntersection; path?: never; query: { /** @@ -2674,27 +2672,27 @@ export type PostGetPolylineIntersectionData_api = { url: "/grid3d/get_polyline_intersection"; }; -export type PostGetPolylineIntersectionErrors_api = { +export type PostGetPolylineIntersectionErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type PostGetPolylineIntersectionError_api = - PostGetPolylineIntersectionErrors_api[keyof PostGetPolylineIntersectionErrors_api]; +export type PostGetPolylineIntersectionError = + PostGetPolylineIntersectionErrors[keyof PostGetPolylineIntersectionErrors]; -export type PostGetPolylineIntersectionResponses_api = { +export type PostGetPolylineIntersectionResponses = { /** * Successful Response */ - 200: PolylineIntersection_api; + 200: PolylineIntersection; }; -export type PostGetPolylineIntersectionResponse_api = - PostGetPolylineIntersectionResponses_api[keyof PostGetPolylineIntersectionResponses_api]; +export type PostGetPolylineIntersectionResponse = + PostGetPolylineIntersectionResponses[keyof PostGetPolylineIntersectionResponses]; -export type GetRealizationFlowNetworkData_api = { +export type GetRealizationFlowNetworkData = { body?: never; path?: never; query: { @@ -2713,35 +2711,35 @@ export type GetRealizationFlowNetworkData_api = { /** * Resampling frequency */ - resampling_frequency: Frequency_api; + resampling_frequency: Frequency; /** * Node types */ - node_type_set: Array; + node_type_set: Array; }; url: "/flow_network/realization_flow_network/"; }; -export type GetRealizationFlowNetworkErrors_api = { +export type GetRealizationFlowNetworkErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetRealizationFlowNetworkError_api = GetRealizationFlowNetworkErrors_api[keyof GetRealizationFlowNetworkErrors_api]; +export type GetRealizationFlowNetworkError = GetRealizationFlowNetworkErrors[keyof GetRealizationFlowNetworkErrors]; -export type GetRealizationFlowNetworkResponses_api = { +export type GetRealizationFlowNetworkResponses = { /** * Successful Response */ - 200: FlowNetworkData_api; + 200: FlowNetworkData; }; -export type GetRealizationFlowNetworkResponse_api = - GetRealizationFlowNetworkResponses_api[keyof GetRealizationFlowNetworkResponses_api]; +export type GetRealizationFlowNetworkResponse = + GetRealizationFlowNetworkResponses[keyof GetRealizationFlowNetworkResponses]; -export type GetTableDataData_api = { +export type GetTableDataData = { body?: never; path?: never; query: { @@ -2761,25 +2759,25 @@ export type GetTableDataData_api = { url: "/pvt/table_data/"; }; -export type GetTableDataErrors_api = { +export type GetTableDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetTableDataError_api = GetTableDataErrors_api[keyof GetTableDataErrors_api]; +export type GetTableDataError = GetTableDataErrors[keyof GetTableDataErrors]; -export type GetTableDataResponses_api = { +export type GetTableDataResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetTableDataResponse_api = GetTableDataResponses_api[keyof GetTableDataResponses_api]; +export type GetTableDataResponse = GetTableDataResponses[keyof GetTableDataResponses]; -export type GetWellCompletionsDataData_api = { +export type GetWellCompletionsDataData = { body?: never; path?: never; query: { @@ -2799,25 +2797,25 @@ export type GetWellCompletionsDataData_api = { url: "/well_completions/well_completions_data/"; }; -export type GetWellCompletionsDataErrors_api = { +export type GetWellCompletionsDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetWellCompletionsDataError_api = GetWellCompletionsDataErrors_api[keyof GetWellCompletionsDataErrors_api]; +export type GetWellCompletionsDataError = GetWellCompletionsDataErrors[keyof GetWellCompletionsDataErrors]; -export type GetWellCompletionsDataResponses_api = { +export type GetWellCompletionsDataResponses = { /** * Successful Response */ - 200: WellCompletionsData_api; + 200: WellCompletionsData; }; -export type GetWellCompletionsDataResponse_api = GetWellCompletionsDataResponses_api[keyof GetWellCompletionsDataResponses_api]; +export type GetWellCompletionsDataResponse = GetWellCompletionsDataResponses[keyof GetWellCompletionsDataResponses]; -export type GetDrilledWellboreHeadersData_api = { +export type GetDrilledWellboreHeadersData = { body?: never; path?: never; query: { @@ -2829,26 +2827,26 @@ export type GetDrilledWellboreHeadersData_api = { url: "/well/drilled_wellbore_headers/"; }; -export type GetDrilledWellboreHeadersErrors_api = { +export type GetDrilledWellboreHeadersErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetDrilledWellboreHeadersError_api = GetDrilledWellboreHeadersErrors_api[keyof GetDrilledWellboreHeadersErrors_api]; +export type GetDrilledWellboreHeadersError = GetDrilledWellboreHeadersErrors[keyof GetDrilledWellboreHeadersErrors]; -export type GetDrilledWellboreHeadersResponses_api = { +export type GetDrilledWellboreHeadersResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetDrilledWellboreHeadersResponse_api = - GetDrilledWellboreHeadersResponses_api[keyof GetDrilledWellboreHeadersResponses_api]; +export type GetDrilledWellboreHeadersResponse = + GetDrilledWellboreHeadersResponses[keyof GetDrilledWellboreHeadersResponses]; -export type GetWellTrajectoriesData_api = { +export type GetWellTrajectoriesData = { body?: never; path?: never; query: { @@ -2864,25 +2862,25 @@ export type GetWellTrajectoriesData_api = { url: "/well/well_trajectories/"; }; -export type GetWellTrajectoriesErrors_api = { +export type GetWellTrajectoriesErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetWellTrajectoriesError_api = GetWellTrajectoriesErrors_api[keyof GetWellTrajectoriesErrors_api]; +export type GetWellTrajectoriesError = GetWellTrajectoriesErrors[keyof GetWellTrajectoriesErrors]; -export type GetWellTrajectoriesResponses_api = { +export type GetWellTrajectoriesResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellTrajectoriesResponse_api = GetWellTrajectoriesResponses_api[keyof GetWellTrajectoriesResponses_api]; +export type GetWellTrajectoriesResponse = GetWellTrajectoriesResponses[keyof GetWellTrajectoriesResponses]; -export type GetWellborePickIdentifiersData_api = { +export type GetWellborePickIdentifiersData = { body?: never; path?: never; query: { @@ -2894,26 +2892,26 @@ export type GetWellborePickIdentifiersData_api = { url: "/well/wellbore_pick_identifiers/"; }; -export type GetWellborePickIdentifiersErrors_api = { +export type GetWellborePickIdentifiersErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetWellborePickIdentifiersError_api = GetWellborePickIdentifiersErrors_api[keyof GetWellborePickIdentifiersErrors_api]; +export type GetWellborePickIdentifiersError = GetWellborePickIdentifiersErrors[keyof GetWellborePickIdentifiersErrors]; -export type GetWellborePickIdentifiersResponses_api = { +export type GetWellborePickIdentifiersResponses = { /** * Successful Response */ 200: Array; }; -export type GetWellborePickIdentifiersResponse_api = - GetWellborePickIdentifiersResponses_api[keyof GetWellborePickIdentifiersResponses_api]; +export type GetWellborePickIdentifiersResponse = + GetWellborePickIdentifiersResponses[keyof GetWellborePickIdentifiersResponses]; -export type GetWellborePicksForPickIdentifierData_api = { +export type GetWellborePicksForPickIdentifierData = { body?: never; path?: never; query: { @@ -2929,27 +2927,27 @@ export type GetWellborePicksForPickIdentifierData_api = { url: "/well/wellbore_picks_for_pick_identifier/"; }; -export type GetWellborePicksForPickIdentifierErrors_api = { +export type GetWellborePicksForPickIdentifierErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetWellborePicksForPickIdentifierError_api = - GetWellborePicksForPickIdentifierErrors_api[keyof GetWellborePicksForPickIdentifierErrors_api]; +export type GetWellborePicksForPickIdentifierError = + GetWellborePicksForPickIdentifierErrors[keyof GetWellborePicksForPickIdentifierErrors]; -export type GetWellborePicksForPickIdentifierResponses_api = { +export type GetWellborePicksForPickIdentifierResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellborePicksForPickIdentifierResponse_api = - GetWellborePicksForPickIdentifierResponses_api[keyof GetWellborePicksForPickIdentifierResponses_api]; +export type GetWellborePicksForPickIdentifierResponse = + GetWellborePicksForPickIdentifierResponses[keyof GetWellborePicksForPickIdentifierResponses]; -export type GetWellborePicksForWellboreData_api = { +export type GetWellborePicksForWellboreData = { body?: never; path?: never; query: { @@ -2961,27 +2959,27 @@ export type GetWellborePicksForWellboreData_api = { url: "/well/wellbore_picks_for_wellbore/"; }; -export type GetWellborePicksForWellboreErrors_api = { +export type GetWellborePicksForWellboreErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetWellborePicksForWellboreError_api = - GetWellborePicksForWellboreErrors_api[keyof GetWellborePicksForWellboreErrors_api]; +export type GetWellborePicksForWellboreError = + GetWellborePicksForWellboreErrors[keyof GetWellborePicksForWellboreErrors]; -export type GetWellborePicksForWellboreResponses_api = { +export type GetWellborePicksForWellboreResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellborePicksForWellboreResponse_api = - GetWellborePicksForWellboreResponses_api[keyof GetWellborePicksForWellboreResponses_api]; +export type GetWellborePicksForWellboreResponse = + GetWellborePicksForWellboreResponses[keyof GetWellborePicksForWellboreResponses]; -export type GetWellborePicksInStratColumnData_api = { +export type GetWellborePicksInStratColumnData = { body?: never; path?: never; query: { @@ -2997,27 +2995,27 @@ export type GetWellborePicksInStratColumnData_api = { url: "/well/wellbore_picks_in_strat_column"; }; -export type GetWellborePicksInStratColumnErrors_api = { +export type GetWellborePicksInStratColumnErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetWellborePicksInStratColumnError_api = - GetWellborePicksInStratColumnErrors_api[keyof GetWellborePicksInStratColumnErrors_api]; +export type GetWellborePicksInStratColumnError = + GetWellborePicksInStratColumnErrors[keyof GetWellborePicksInStratColumnErrors]; -export type GetWellborePicksInStratColumnResponses_api = { +export type GetWellborePicksInStratColumnResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellborePicksInStratColumnResponse_api = - GetWellborePicksInStratColumnResponses_api[keyof GetWellborePicksInStratColumnResponses_api]; +export type GetWellborePicksInStratColumnResponse = + GetWellborePicksInStratColumnResponses[keyof GetWellborePicksInStratColumnResponses]; -export type GetWellboreCompletionsData_api = { +export type GetWellboreCompletionsData = { body?: never; path?: never; query: { @@ -3029,25 +3027,25 @@ export type GetWellboreCompletionsData_api = { url: "/well/wellbore_completions/"; }; -export type GetWellboreCompletionsErrors_api = { +export type GetWellboreCompletionsErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetWellboreCompletionsError_api = GetWellboreCompletionsErrors_api[keyof GetWellboreCompletionsErrors_api]; +export type GetWellboreCompletionsError = GetWellboreCompletionsErrors[keyof GetWellboreCompletionsErrors]; -export type GetWellboreCompletionsResponses_api = { +export type GetWellboreCompletionsResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellboreCompletionsResponse_api = GetWellboreCompletionsResponses_api[keyof GetWellboreCompletionsResponses_api]; +export type GetWellboreCompletionsResponse = GetWellboreCompletionsResponses[keyof GetWellboreCompletionsResponses]; -export type GetWellboreCasingsData_api = { +export type GetWellboreCasingsData = { body?: never; path?: never; query: { @@ -3059,25 +3057,25 @@ export type GetWellboreCasingsData_api = { url: "/well/wellbore_casings/"; }; -export type GetWellboreCasingsErrors_api = { +export type GetWellboreCasingsErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetWellboreCasingsError_api = GetWellboreCasingsErrors_api[keyof GetWellboreCasingsErrors_api]; +export type GetWellboreCasingsError = GetWellboreCasingsErrors[keyof GetWellboreCasingsErrors]; -export type GetWellboreCasingsResponses_api = { +export type GetWellboreCasingsResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellboreCasingsResponse_api = GetWellboreCasingsResponses_api[keyof GetWellboreCasingsResponses_api]; +export type GetWellboreCasingsResponse = GetWellboreCasingsResponses[keyof GetWellboreCasingsResponses]; -export type GetWellborePerforationsData_api = { +export type GetWellborePerforationsData = { body?: never; path?: never; query: { @@ -3089,25 +3087,25 @@ export type GetWellborePerforationsData_api = { url: "/well/wellbore_perforations/"; }; -export type GetWellborePerforationsErrors_api = { +export type GetWellborePerforationsErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetWellborePerforationsError_api = GetWellborePerforationsErrors_api[keyof GetWellborePerforationsErrors_api]; +export type GetWellborePerforationsError = GetWellborePerforationsErrors[keyof GetWellborePerforationsErrors]; -export type GetWellborePerforationsResponses_api = { +export type GetWellborePerforationsResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellborePerforationsResponse_api = GetWellborePerforationsResponses_api[keyof GetWellborePerforationsResponses_api]; +export type GetWellborePerforationsResponse = GetWellborePerforationsResponses[keyof GetWellborePerforationsResponses]; -export type GetWellboreLogCurveHeadersData_api = { +export type GetWellboreLogCurveHeadersData = { body?: never; path?: never; query: { @@ -3118,31 +3116,31 @@ export type GetWellboreLogCurveHeadersData_api = { /** * Sources to fetch well-logs from. */ - sources?: Array; + sources?: Array; }; url: "/well/wellbore_log_curve_headers/"; }; -export type GetWellboreLogCurveHeadersErrors_api = { +export type GetWellboreLogCurveHeadersErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetWellboreLogCurveHeadersError_api = GetWellboreLogCurveHeadersErrors_api[keyof GetWellboreLogCurveHeadersErrors_api]; +export type GetWellboreLogCurveHeadersError = GetWellboreLogCurveHeadersErrors[keyof GetWellboreLogCurveHeadersErrors]; -export type GetWellboreLogCurveHeadersResponses_api = { +export type GetWellboreLogCurveHeadersResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellboreLogCurveHeadersResponse_api = - GetWellboreLogCurveHeadersResponses_api[keyof GetWellboreLogCurveHeadersResponses_api]; +export type GetWellboreLogCurveHeadersResponse = + GetWellboreLogCurveHeadersResponses[keyof GetWellboreLogCurveHeadersResponses]; -export type GetLogCurveDataData_api = { +export type GetLogCurveDataData = { body?: never; path?: never; query: { @@ -3161,30 +3159,30 @@ export type GetLogCurveDataData_api = { /** * Source to fetch well-logs from. */ - source?: WellLogCurveSourceEnum_api; + source?: WellLogCurveSourceEnum; }; url: "/well/log_curve_data/"; }; -export type GetLogCurveDataErrors_api = { +export type GetLogCurveDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetLogCurveDataError_api = GetLogCurveDataErrors_api[keyof GetLogCurveDataErrors_api]; +export type GetLogCurveDataError = GetLogCurveDataErrors[keyof GetLogCurveDataErrors]; -export type GetLogCurveDataResponses_api = { +export type GetLogCurveDataResponses = { /** * Successful Response */ - 200: WellboreLogCurveData_api; + 200: WellboreLogCurveData; }; -export type GetLogCurveDataResponse_api = GetLogCurveDataResponses_api[keyof GetLogCurveDataResponses_api]; +export type GetLogCurveDataResponse = GetLogCurveDataResponses[keyof GetLogCurveDataResponses]; -export type GetSeismicCubeMetaListData_api = { +export type GetSeismicCubeMetaListData = { body?: never; path?: never; query: { @@ -3200,25 +3198,25 @@ export type GetSeismicCubeMetaListData_api = { url: "/seismic/seismic_cube_meta_list/"; }; -export type GetSeismicCubeMetaListErrors_api = { +export type GetSeismicCubeMetaListErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetSeismicCubeMetaListError_api = GetSeismicCubeMetaListErrors_api[keyof GetSeismicCubeMetaListErrors_api]; +export type GetSeismicCubeMetaListError = GetSeismicCubeMetaListErrors[keyof GetSeismicCubeMetaListErrors]; -export type GetSeismicCubeMetaListResponses_api = { +export type GetSeismicCubeMetaListResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetSeismicCubeMetaListResponse_api = GetSeismicCubeMetaListResponses_api[keyof GetSeismicCubeMetaListResponses_api]; +export type GetSeismicCubeMetaListResponse = GetSeismicCubeMetaListResponses[keyof GetSeismicCubeMetaListResponses]; -export type GetInlineSliceData_api = { +export type GetInlineSliceData = { body?: never; path?: never; query: { @@ -3254,25 +3252,25 @@ export type GetInlineSliceData_api = { url: "/seismic/get_inline_slice/"; }; -export type GetInlineSliceErrors_api = { +export type GetInlineSliceErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetInlineSliceError_api = GetInlineSliceErrors_api[keyof GetInlineSliceErrors_api]; +export type GetInlineSliceError = GetInlineSliceErrors[keyof GetInlineSliceErrors]; -export type GetInlineSliceResponses_api = { +export type GetInlineSliceResponses = { /** * Successful Response */ - 200: SeismicSliceData_api; + 200: SeismicSliceData; }; -export type GetInlineSliceResponse_api = GetInlineSliceResponses_api[keyof GetInlineSliceResponses_api]; +export type GetInlineSliceResponse = GetInlineSliceResponses[keyof GetInlineSliceResponses]; -export type GetCrosslineSliceData_api = { +export type GetCrosslineSliceData = { body?: never; path?: never; query: { @@ -3308,25 +3306,25 @@ export type GetCrosslineSliceData_api = { url: "/seismic/get_crossline_slice/"; }; -export type GetCrosslineSliceErrors_api = { +export type GetCrosslineSliceErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetCrosslineSliceError_api = GetCrosslineSliceErrors_api[keyof GetCrosslineSliceErrors_api]; +export type GetCrosslineSliceError = GetCrosslineSliceErrors[keyof GetCrosslineSliceErrors]; -export type GetCrosslineSliceResponses_api = { +export type GetCrosslineSliceResponses = { /** * Successful Response */ - 200: SeismicSliceData_api; + 200: SeismicSliceData; }; -export type GetCrosslineSliceResponse_api = GetCrosslineSliceResponses_api[keyof GetCrosslineSliceResponses_api]; +export type GetCrosslineSliceResponse = GetCrosslineSliceResponses[keyof GetCrosslineSliceResponses]; -export type GetDepthSliceData_api = { +export type GetDepthSliceData = { body?: never; path?: never; query: { @@ -3362,26 +3360,26 @@ export type GetDepthSliceData_api = { url: "/seismic/get_depth_slice/"; }; -export type GetDepthSliceErrors_api = { +export type GetDepthSliceErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetDepthSliceError_api = GetDepthSliceErrors_api[keyof GetDepthSliceErrors_api]; +export type GetDepthSliceError = GetDepthSliceErrors[keyof GetDepthSliceErrors]; -export type GetDepthSliceResponses_api = { +export type GetDepthSliceResponses = { /** * Successful Response */ - 200: SeismicSliceData_api; + 200: SeismicSliceData; }; -export type GetDepthSliceResponse_api = GetDepthSliceResponses_api[keyof GetDepthSliceResponses_api]; +export type GetDepthSliceResponse = GetDepthSliceResponses[keyof GetDepthSliceResponses]; -export type PostGetSeismicFenceData_api = { - body: BodyPostGetSeismicFence_api; +export type PostGetSeismicFenceData = { + body: BodyPostGetSeismicFence; path?: never; query: { /** @@ -3412,25 +3410,25 @@ export type PostGetSeismicFenceData_api = { url: "/seismic/get_seismic_fence/"; }; -export type PostGetSeismicFenceErrors_api = { +export type PostGetSeismicFenceErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type PostGetSeismicFenceError_api = PostGetSeismicFenceErrors_api[keyof PostGetSeismicFenceErrors_api]; +export type PostGetSeismicFenceError = PostGetSeismicFenceErrors[keyof PostGetSeismicFenceErrors]; -export type PostGetSeismicFenceResponses_api = { +export type PostGetSeismicFenceResponses = { /** * Successful Response */ - 200: SeismicFenceData_api; + 200: SeismicFenceData; }; -export type PostGetSeismicFenceResponse_api = PostGetSeismicFenceResponses_api[keyof PostGetSeismicFenceResponses_api]; +export type PostGetSeismicFenceResponse = PostGetSeismicFenceResponses[keyof PostGetSeismicFenceResponses]; -export type GetPolygonsDirectoryData_api = { +export type GetPolygonsDirectoryData = { body?: never; path?: never; query: { @@ -3446,25 +3444,25 @@ export type GetPolygonsDirectoryData_api = { url: "/polygons/polygons_directory/"; }; -export type GetPolygonsDirectoryErrors_api = { +export type GetPolygonsDirectoryErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetPolygonsDirectoryError_api = GetPolygonsDirectoryErrors_api[keyof GetPolygonsDirectoryErrors_api]; +export type GetPolygonsDirectoryError = GetPolygonsDirectoryErrors[keyof GetPolygonsDirectoryErrors]; -export type GetPolygonsDirectoryResponses_api = { +export type GetPolygonsDirectoryResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetPolygonsDirectoryResponse_api = GetPolygonsDirectoryResponses_api[keyof GetPolygonsDirectoryResponses_api]; +export type GetPolygonsDirectoryResponse = GetPolygonsDirectoryResponses[keyof GetPolygonsDirectoryResponses]; -export type GetPolygonsDataData_api = { +export type GetPolygonsDataData = { body?: never; path?: never; query: { @@ -3492,25 +3490,25 @@ export type GetPolygonsDataData_api = { url: "/polygons/polygons_data/"; }; -export type GetPolygonsDataErrors_api = { +export type GetPolygonsDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetPolygonsDataError_api = GetPolygonsDataErrors_api[keyof GetPolygonsDataErrors_api]; +export type GetPolygonsDataError = GetPolygonsDataErrors[keyof GetPolygonsDataErrors]; -export type GetPolygonsDataResponses_api = { +export type GetPolygonsDataResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetPolygonsDataResponse_api = GetPolygonsDataResponses_api[keyof GetPolygonsDataResponses_api]; +export type GetPolygonsDataResponse = GetPolygonsDataResponses[keyof GetPolygonsDataResponses]; -export type GetUserPhotoData_api = { +export type GetUserPhotoData = { body?: never; path?: never; query: { @@ -3522,25 +3520,25 @@ export type GetUserPhotoData_api = { url: "/graph/user_photo/"; }; -export type GetUserPhotoErrors_api = { +export type GetUserPhotoErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetUserPhotoError_api = GetUserPhotoErrors_api[keyof GetUserPhotoErrors_api]; +export type GetUserPhotoError = GetUserPhotoErrors[keyof GetUserPhotoErrors]; -export type GetUserPhotoResponses_api = { +export type GetUserPhotoResponses = { /** * Successful Response */ - 200: GraphUserPhoto_api; + 200: GraphUserPhoto; }; -export type GetUserPhotoResponse_api = GetUserPhotoResponses_api[keyof GetUserPhotoResponses_api]; +export type GetUserPhotoResponse = GetUserPhotoResponses[keyof GetUserPhotoResponses]; -export type GetObservationsData_api = { +export type GetObservationsData = { body?: never; path?: never; query: { @@ -3552,25 +3550,25 @@ export type GetObservationsData_api = { url: "/observations/observations/"; }; -export type GetObservationsErrors_api = { +export type GetObservationsErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetObservationsError_api = GetObservationsErrors_api[keyof GetObservationsErrors_api]; +export type GetObservationsError = GetObservationsErrors[keyof GetObservationsErrors]; -export type GetObservationsResponses_api = { +export type GetObservationsResponses = { /** * Successful Response */ - 200: Observations_api; + 200: Observations; }; -export type GetObservationsResponse_api = GetObservationsResponses_api[keyof GetObservationsResponses_api]; +export type GetObservationsResponse = GetObservationsResponses[keyof GetObservationsResponses]; -export type GetTableDefinitionData_api = { +export type GetTableDefinitionData = { body?: never; path?: never; query: { @@ -3586,25 +3584,25 @@ export type GetTableDefinitionData_api = { url: "/rft/table_definition"; }; -export type GetTableDefinitionErrors_api = { +export type GetTableDefinitionErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetTableDefinitionError_api = GetTableDefinitionErrors_api[keyof GetTableDefinitionErrors_api]; +export type GetTableDefinitionError = GetTableDefinitionErrors[keyof GetTableDefinitionErrors]; -export type GetTableDefinitionResponses_api = { +export type GetTableDefinitionResponses = { /** * Successful Response */ - 200: RftTableDefinition_api; + 200: RftTableDefinition; }; -export type GetTableDefinitionResponse_api = GetTableDefinitionResponses_api[keyof GetTableDefinitionResponses_api]; +export type GetTableDefinitionResponse = GetTableDefinitionResponses[keyof GetTableDefinitionResponses]; -export type GetRealizationDataData_api = { +export type GetRealizationDataData = { body?: never; path?: never; query: { @@ -3636,25 +3634,25 @@ export type GetRealizationDataData_api = { url: "/rft/realization_data"; }; -export type GetRealizationDataErrors_api = { +export type GetRealizationDataErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetRealizationDataError_api = GetRealizationDataErrors_api[keyof GetRealizationDataErrors_api]; +export type GetRealizationDataError = GetRealizationDataErrors[keyof GetRealizationDataErrors]; -export type GetRealizationDataResponses_api = { +export type GetRealizationDataResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetRealizationDataResponse_api = GetRealizationDataResponses_api[keyof GetRealizationDataResponses_api]; +export type GetRealizationDataResponse = GetRealizationDataResponses[keyof GetRealizationDataResponses]; -export type GetVfpTableNamesData_api = { +export type GetVfpTableNamesData = { body?: never; path?: never; query: { @@ -3674,25 +3672,25 @@ export type GetVfpTableNamesData_api = { url: "/vfp/vfp_table_names/"; }; -export type GetVfpTableNamesErrors_api = { +export type GetVfpTableNamesErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetVfpTableNamesError_api = GetVfpTableNamesErrors_api[keyof GetVfpTableNamesErrors_api]; +export type GetVfpTableNamesError = GetVfpTableNamesErrors[keyof GetVfpTableNamesErrors]; -export type GetVfpTableNamesResponses_api = { +export type GetVfpTableNamesResponses = { /** * Successful Response */ 200: Array; }; -export type GetVfpTableNamesResponse_api = GetVfpTableNamesResponses_api[keyof GetVfpTableNamesResponses_api]; +export type GetVfpTableNamesResponse = GetVfpTableNamesResponses[keyof GetVfpTableNamesResponses]; -export type GetVfpTableData_api = { +export type GetVfpTableData = { body?: never; path?: never; query: { @@ -3716,25 +3714,25 @@ export type GetVfpTableData_api = { url: "/vfp/vfp_table/"; }; -export type GetVfpTableErrors_api = { +export type GetVfpTableErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetVfpTableError_api = GetVfpTableErrors_api[keyof GetVfpTableErrors_api]; +export type GetVfpTableError = GetVfpTableErrors[keyof GetVfpTableErrors]; -export type GetVfpTableResponses_api = { +export type GetVfpTableResponses = { /** * Successful Response */ - 200: VfpProdTable_api | VfpInjTable_api; + 200: VfpProdTable | VfpInjTable; }; -export type GetVfpTableResponse_api = GetVfpTableResponses_api[keyof GetVfpTableResponses_api]; +export type GetVfpTableResponse = GetVfpTableResponses[keyof GetVfpTableResponses]; -export type LoginRouteData_api = { +export type LoginRouteData = { body?: never; path?: never; query?: { @@ -3743,85 +3741,85 @@ export type LoginRouteData_api = { url: "/login"; }; -export type LoginRouteErrors_api = { +export type LoginRouteErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type LoginRouteError_api = LoginRouteErrors_api[keyof LoginRouteErrors_api]; +export type LoginRouteError = LoginRouteErrors[keyof LoginRouteErrors]; -export type LoginRouteResponses_api = { +export type LoginRouteResponses = { /** * Successful Response */ 200: unknown; }; -export type AuthorizedCallbackRouteData_api = { +export type AuthorizedCallbackRouteData = { body?: never; path?: never; query?: never; url: "/auth-callback"; }; -export type AuthorizedCallbackRouteResponses_api = { +export type AuthorizedCallbackRouteResponses = { /** * Successful Response */ 200: unknown; }; -export type GetAliveData_api = { +export type GetAliveData = { body?: never; path?: never; query?: never; url: "/alive"; }; -export type GetAliveResponses_api = { +export type GetAliveResponses = { /** * Successful Response */ 200: string; }; -export type GetAliveResponse_api = GetAliveResponses_api[keyof GetAliveResponses_api]; +export type GetAliveResponse = GetAliveResponses[keyof GetAliveResponses]; -export type GetAliveProtectedData_api = { +export type GetAliveProtectedData = { body?: never; path?: never; query?: never; url: "/alive_protected"; }; -export type GetAliveProtectedResponses_api = { +export type GetAliveProtectedResponses = { /** * Successful Response */ 200: string; }; -export type GetAliveProtectedResponse_api = GetAliveProtectedResponses_api[keyof GetAliveProtectedResponses_api]; +export type GetAliveProtectedResponse = GetAliveProtectedResponses[keyof GetAliveProtectedResponses]; -export type PostLogoutData_api = { +export type PostLogoutData = { body?: never; path?: never; query?: never; url: "/logout"; }; -export type PostLogoutResponses_api = { +export type PostLogoutResponses = { /** * Successful Response */ 200: string; }; -export type PostLogoutResponse_api = PostLogoutResponses_api[keyof PostLogoutResponses_api]; +export type PostLogoutResponse = PostLogoutResponses[keyof PostLogoutResponses]; -export type GetLoggedInUserData_api = { +export type GetLoggedInUserData = { body?: never; path?: never; query?: { @@ -3833,36 +3831,36 @@ export type GetLoggedInUserData_api = { url: "/logged_in_user"; }; -export type GetLoggedInUserErrors_api = { +export type GetLoggedInUserErrors = { /** * Validation Error */ - 422: HttpValidationError_api; + 422: HttpValidationError; }; -export type GetLoggedInUserError_api = GetLoggedInUserErrors_api[keyof GetLoggedInUserErrors_api]; +export type GetLoggedInUserError = GetLoggedInUserErrors[keyof GetLoggedInUserErrors]; -export type GetLoggedInUserResponses_api = { +export type GetLoggedInUserResponses = { /** * Successful Response */ - 200: UserInfo_api; + 200: UserInfo; }; -export type GetLoggedInUserResponse_api = GetLoggedInUserResponses_api[keyof GetLoggedInUserResponses_api]; +export type GetLoggedInUserResponse = GetLoggedInUserResponses[keyof GetLoggedInUserResponses]; -export type RootData_api = { +export type RootData = { body?: never; path?: never; query?: never; url: "/"; }; -export type RootResponses_api = { +export type RootResponses = { /** * Successful Response */ 200: string; }; -export type RootResponse_api = RootResponses_api[keyof RootResponses_api]; +export type RootResponse = RootResponses[keyof RootResponses]; From 3b803ccaa108e1bcbdff9894459d69a09bba8709 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 2 May 2025 14:43:03 +0200 Subject: [PATCH 75/97] wip --- .../api/autogen/@tanstack/react-query.gen.ts | 499 +++--- frontend/src/api/autogen/index.ts | 2 + frontend/src/api/autogen/sdk.gen.ts | 719 ++++---- frontend/src/api/autogen/types.gen.ts | 1444 ++++++++--------- .../ExternalSettingController.ts | 5 + .../SettingManager/SettingManager.ts | 77 +- .../SettingManagerComponent.tsx | 1 - 7 files changed, 1381 insertions(+), 1366 deletions(-) diff --git a/frontend/src/api/autogen/@tanstack/react-query.gen.ts b/frontend/src/api/autogen/@tanstack/react-query.gen.ts index 24ba7b4ab..2f0de657e 100644 --- a/frontend/src/api/autogen/@tanstack/react-query.gen.ts +++ b/frontend/src/api/autogen/@tanstack/react-query.gen.ts @@ -2,90 +2,8 @@ import type { Options } from "@hey-api/client-axios"; import { queryOptions, type UseMutationOptions, type DefaultError } from "@tanstack/react-query"; -import type { - GetFieldsData, - GetCasesData, - GetEnsemblesData, - GetEnsembleDetailsData, - GetVectorListData, - GetDeltaEnsembleVectorListData, - GetRealizationsVectorDataData, - GetDeltaEnsembleRealizationsVectorDataData, - GetTimestampsListData, - GetHistoricalVectorDataData, - GetStatisticalVectorDataData, - GetDeltaEnsembleStatisticalVectorDataData, - GetStatisticalVectorDataPerSensitivityData, - GetRealizationVectorAtTimestampData, - GetTableDefinitionsData, - PostGetAggregatedPerRealizationTableDataData, - PostGetAggregatedPerRealizationTableDataError, - PostGetAggregatedPerRealizationTableDataResponse, - PostGetAggregatedStatisticalTableDataData, - PostGetAggregatedStatisticalTableDataError, - PostGetAggregatedStatisticalTableDataResponse, - GetRealizationSurfacesMetadataData, - GetObservedSurfacesMetadataData, - GetSurfaceDataData, - PostGetSurfaceIntersectionData, - PostGetSurfaceIntersectionError, - PostGetSurfaceIntersectionResponse, - PostGetSampleSurfaceInPointsData, - PostGetSampleSurfaceInPointsError, - PostGetSampleSurfaceInPointsResponse, - GetDeltaSurfaceDataData, - GetMisfitSurfaceDataData, - GetWellboreStratigraphicColumnsData, - GetStratigraphicUnitsData, - GetParameterNamesAndDescriptionData, - GetParameterData, - GetParametersData, - GetIsSensitivityRunData, - GetSensitivitiesData, - GetGridModelsInfoData, - GetGridSurfaceData, - GetGridParameterData, - PostGetPolylineIntersectionData, - PostGetPolylineIntersectionError, - PostGetPolylineIntersectionResponse, - GetRealizationFlowNetworkData, - GetTableDataData, - GetWellCompletionsDataData, - GetDrilledWellboreHeadersData, - GetWellTrajectoriesData, - GetWellborePickIdentifiersData, - GetWellborePicksForPickIdentifierData, - GetWellborePicksForWellboreData, - GetWellborePicksInStratColumnData, - GetWellboreCompletionsData, - GetWellboreCasingsData, - GetWellborePerforationsData, - GetWellboreLogCurveHeadersData, - GetLogCurveDataData, - GetSeismicCubeMetaListData, - GetInlineSliceData, - GetCrosslineSliceData, - GetDepthSliceData, - PostGetSeismicFenceData, - PostGetSeismicFenceError, - PostGetSeismicFenceResponse, - GetPolygonsDirectoryData, - GetPolygonsDataData, - GetUserPhotoData, - GetObservationsData, - GetTableDefinitionData, - GetRealizationDataData, - GetVfpTableNamesData, - GetVfpTableData, - LoginRouteData, - AuthorizedCallbackRouteData, - GetAliveData, - GetAliveProtectedData, - PostLogoutData, - PostLogoutResponse, - GetLoggedInUserData, - RootData, -} from "../types.gen"; +import type { AxiosError } from "axios"; + import { getFields, getCases, @@ -158,7 +76,90 @@ import { root, client, } from "../sdk.gen"; -import type { AxiosError } from "axios"; +import type { + GetFieldsData_api, + GetCasesData_api, + GetEnsemblesData_api, + GetEnsembleDetailsData_api, + GetVectorListData_api, + GetDeltaEnsembleVectorListData_api, + GetRealizationsVectorDataData_api, + GetDeltaEnsembleRealizationsVectorDataData_api, + GetTimestampsListData_api, + GetHistoricalVectorDataData_api, + GetStatisticalVectorDataData_api, + GetDeltaEnsembleStatisticalVectorDataData_api, + GetStatisticalVectorDataPerSensitivityData_api, + GetRealizationVectorAtTimestampData_api, + GetTableDefinitionsData_api, + PostGetAggregatedPerRealizationTableDataData_api, + PostGetAggregatedPerRealizationTableDataError_api, + PostGetAggregatedPerRealizationTableDataResponse_api, + PostGetAggregatedStatisticalTableDataData_api, + PostGetAggregatedStatisticalTableDataError_api, + PostGetAggregatedStatisticalTableDataResponse_api, + GetRealizationSurfacesMetadataData_api, + GetObservedSurfacesMetadataData_api, + GetSurfaceDataData_api, + PostGetSurfaceIntersectionData_api, + PostGetSurfaceIntersectionError_api, + PostGetSurfaceIntersectionResponse_api, + PostGetSampleSurfaceInPointsData_api, + PostGetSampleSurfaceInPointsError_api, + PostGetSampleSurfaceInPointsResponse_api, + GetDeltaSurfaceDataData_api, + GetMisfitSurfaceDataData_api, + GetWellboreStratigraphicColumnsData_api, + GetStratigraphicUnitsData_api, + GetParameterNamesAndDescriptionData_api, + GetParameterData_api, + GetParametersData_api, + GetIsSensitivityRunData_api, + GetSensitivitiesData_api, + GetGridModelsInfoData_api, + GetGridSurfaceData_api, + GetGridParameterData_api, + PostGetPolylineIntersectionData_api, + PostGetPolylineIntersectionError_api, + PostGetPolylineIntersectionResponse_api, + GetRealizationFlowNetworkData_api, + GetTableDataData_api, + GetWellCompletionsDataData_api, + GetDrilledWellboreHeadersData_api, + GetWellTrajectoriesData_api, + GetWellborePickIdentifiersData_api, + GetWellborePicksForPickIdentifierData_api, + GetWellborePicksForWellboreData_api, + GetWellborePicksInStratColumnData_api, + GetWellboreCompletionsData_api, + GetWellboreCasingsData_api, + GetWellborePerforationsData_api, + GetWellboreLogCurveHeadersData_api, + GetLogCurveDataData_api, + GetSeismicCubeMetaListData_api, + GetInlineSliceData_api, + GetCrosslineSliceData_api, + GetDepthSliceData_api, + PostGetSeismicFenceData_api, + PostGetSeismicFenceError_api, + PostGetSeismicFenceResponse_api, + GetPolygonsDirectoryData_api, + GetPolygonsDataData_api, + GetUserPhotoData_api, + GetObservationsData_api, + GetTableDefinitionData_api, + GetRealizationDataData_api, + GetVfpTableNamesData_api, + GetVfpTableData_api, + LoginRouteData_api, + AuthorizedCallbackRouteData_api, + GetAliveData_api, + GetAliveProtectedData_api, + PostLogoutData_api, + PostLogoutResponse_api, + GetLoggedInUserData_api, + RootData_api, +} from "../types.gen"; type QueryKey = [ Pick & { @@ -194,9 +195,9 @@ const createQueryKey = ( return params; }; -export const getFieldsQueryKey = (options?: Options) => [createQueryKey("getFields", options)]; +export const getFieldsQueryKey = (options?: Options) => [createQueryKey("getFields", options)]; -export const getFieldsOptions = (options?: Options) => { +export const getFieldsOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getFields({ @@ -211,9 +212,9 @@ export const getFieldsOptions = (options?: Options) => { }); }; -export const getCasesQueryKey = (options: Options) => [createQueryKey("getCases", options)]; +export const getCasesQueryKey = (options: Options) => [createQueryKey("getCases", options)]; -export const getCasesOptions = (options: Options) => { +export const getCasesOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getCases({ @@ -228,9 +229,9 @@ export const getCasesOptions = (options: Options) => { }); }; -export const getEnsemblesQueryKey = (options: Options) => [createQueryKey("getEnsembles", options)]; +export const getEnsemblesQueryKey = (options: Options) => [createQueryKey("getEnsembles", options)]; -export const getEnsemblesOptions = (options: Options) => { +export const getEnsemblesOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getEnsembles({ @@ -245,11 +246,11 @@ export const getEnsemblesOptions = (options: Options) => { }); }; -export const getEnsembleDetailsQueryKey = (options: Options) => [ +export const getEnsembleDetailsQueryKey = (options: Options) => [ createQueryKey("getEnsembleDetails", options), ]; -export const getEnsembleDetailsOptions = (options: Options) => { +export const getEnsembleDetailsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getEnsembleDetails({ @@ -264,11 +265,11 @@ export const getEnsembleDetailsOptions = (options: Options) => [ +export const getVectorListQueryKey = (options: Options) => [ createQueryKey("getVectorList", options), ]; -export const getVectorListOptions = (options: Options) => { +export const getVectorListOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getVectorList({ @@ -283,11 +284,11 @@ export const getVectorListOptions = (options: Options) => { }); }; -export const getDeltaEnsembleVectorListQueryKey = (options: Options) => [ +export const getDeltaEnsembleVectorListQueryKey = (options: Options) => [ createQueryKey("getDeltaEnsembleVectorList", options), ]; -export const getDeltaEnsembleVectorListOptions = (options: Options) => { +export const getDeltaEnsembleVectorListOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getDeltaEnsembleVectorList({ @@ -302,11 +303,11 @@ export const getDeltaEnsembleVectorListOptions = (options: Options) => [ +export const getRealizationsVectorDataQueryKey = (options: Options) => [ createQueryKey("getRealizationsVectorData", options), ]; -export const getRealizationsVectorDataOptions = (options: Options) => { +export const getRealizationsVectorDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getRealizationsVectorData({ @@ -322,11 +323,11 @@ export const getRealizationsVectorDataOptions = (options: Options, + options: Options, ) => [createQueryKey("getDeltaEnsembleRealizationsVectorData", options)]; export const getDeltaEnsembleRealizationsVectorDataOptions = ( - options: Options, + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { @@ -342,11 +343,11 @@ export const getDeltaEnsembleRealizationsVectorDataOptions = ( }); }; -export const getTimestampsListQueryKey = (options: Options) => [ +export const getTimestampsListQueryKey = (options: Options) => [ createQueryKey("getTimestampsList", options), ]; -export const getTimestampsListOptions = (options: Options) => { +export const getTimestampsListOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getTimestampsList({ @@ -361,11 +362,11 @@ export const getTimestampsListOptions = (options: Options }); }; -export const getHistoricalVectorDataQueryKey = (options: Options) => [ +export const getHistoricalVectorDataQueryKey = (options: Options) => [ createQueryKey("getHistoricalVectorData", options), ]; -export const getHistoricalVectorDataOptions = (options: Options) => { +export const getHistoricalVectorDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getHistoricalVectorData({ @@ -380,11 +381,11 @@ export const getHistoricalVectorDataOptions = (options: Options) => [ +export const getStatisticalVectorDataQueryKey = (options: Options) => [ createQueryKey("getStatisticalVectorData", options), ]; -export const getStatisticalVectorDataOptions = (options: Options) => { +export const getStatisticalVectorDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getStatisticalVectorData({ @@ -400,11 +401,11 @@ export const getStatisticalVectorDataOptions = (options: Options, + options: Options, ) => [createQueryKey("getDeltaEnsembleStatisticalVectorData", options)]; export const getDeltaEnsembleStatisticalVectorDataOptions = ( - options: Options, + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { @@ -421,11 +422,11 @@ export const getDeltaEnsembleStatisticalVectorDataOptions = ( }; export const getStatisticalVectorDataPerSensitivityQueryKey = ( - options: Options, + options: Options, ) => [createQueryKey("getStatisticalVectorDataPerSensitivity", options)]; export const getStatisticalVectorDataPerSensitivityOptions = ( - options: Options, + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { @@ -441,11 +442,11 @@ export const getStatisticalVectorDataPerSensitivityOptions = ( }); }; -export const getRealizationVectorAtTimestampQueryKey = (options: Options) => [ +export const getRealizationVectorAtTimestampQueryKey = (options: Options) => [ createQueryKey("getRealizationVectorAtTimestamp", options), ]; -export const getRealizationVectorAtTimestampOptions = (options: Options) => { +export const getRealizationVectorAtTimestampOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getRealizationVectorAtTimestamp({ @@ -460,11 +461,11 @@ export const getRealizationVectorAtTimestampOptions = (options: Options) => [ +export const getTableDefinitionsQueryKey = (options: Options) => [ createQueryKey("getTableDefinitions", options), ]; -export const getTableDefinitionsOptions = (options: Options) => { +export const getTableDefinitionsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getTableDefinitions({ @@ -480,11 +481,11 @@ export const getTableDefinitionsOptions = (options: Options, + options: Options, ) => [createQueryKey("postGetAggregatedPerRealizationTableData", options)]; export const postGetAggregatedPerRealizationTableDataOptions = ( - options: Options, + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { @@ -501,12 +502,12 @@ export const postGetAggregatedPerRealizationTableDataOptions = ( }; export const postGetAggregatedPerRealizationTableDataMutation = ( - options?: Partial>, + options?: Partial>, ) => { const mutationOptions: UseMutationOptions< - PostGetAggregatedPerRealizationTableDataResponse, - AxiosError, - Options + PostGetAggregatedPerRealizationTableDataResponse_api, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetAggregatedPerRealizationTableData({ @@ -521,11 +522,11 @@ export const postGetAggregatedPerRealizationTableDataMutation = ( }; export const postGetAggregatedStatisticalTableDataQueryKey = ( - options: Options, + options: Options, ) => [createQueryKey("postGetAggregatedStatisticalTableData", options)]; export const postGetAggregatedStatisticalTableDataOptions = ( - options: Options, + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { @@ -542,12 +543,12 @@ export const postGetAggregatedStatisticalTableDataOptions = ( }; export const postGetAggregatedStatisticalTableDataMutation = ( - options?: Partial>, + options?: Partial>, ) => { const mutationOptions: UseMutationOptions< - PostGetAggregatedStatisticalTableDataResponse, - AxiosError, - Options + PostGetAggregatedStatisticalTableDataResponse_api, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetAggregatedStatisticalTableData({ @@ -561,11 +562,11 @@ export const postGetAggregatedStatisticalTableDataMutation = ( return mutationOptions; }; -export const getRealizationSurfacesMetadataQueryKey = (options: Options) => [ +export const getRealizationSurfacesMetadataQueryKey = (options: Options) => [ createQueryKey("getRealizationSurfacesMetadata", options), ]; -export const getRealizationSurfacesMetadataOptions = (options: Options) => { +export const getRealizationSurfacesMetadataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getRealizationSurfacesMetadata({ @@ -580,11 +581,11 @@ export const getRealizationSurfacesMetadataOptions = (options: Options) => [ +export const getObservedSurfacesMetadataQueryKey = (options: Options) => [ createQueryKey("getObservedSurfacesMetadata", options), ]; -export const getObservedSurfacesMetadataOptions = (options: Options) => { +export const getObservedSurfacesMetadataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getObservedSurfacesMetadata({ @@ -599,11 +600,11 @@ export const getObservedSurfacesMetadataOptions = (options: Options) => [ +export const getSurfaceDataQueryKey = (options: Options) => [ createQueryKey("getSurfaceData", options), ]; -export const getSurfaceDataOptions = (options: Options) => { +export const getSurfaceDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getSurfaceData({ @@ -618,11 +619,11 @@ export const getSurfaceDataOptions = (options: Options) => { }); }; -export const postGetSurfaceIntersectionQueryKey = (options: Options) => [ +export const postGetSurfaceIntersectionQueryKey = (options: Options) => [ createQueryKey("postGetSurfaceIntersection", options), ]; -export const postGetSurfaceIntersectionOptions = (options: Options) => { +export const postGetSurfaceIntersectionOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await postGetSurfaceIntersection({ @@ -637,11 +638,11 @@ export const postGetSurfaceIntersectionOptions = (options: Options>) => { +export const postGetSurfaceIntersectionMutation = (options?: Partial>) => { const mutationOptions: UseMutationOptions< - PostGetSurfaceIntersectionResponse, - AxiosError, - Options + PostGetSurfaceIntersectionResponse_api, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetSurfaceIntersection({ @@ -655,11 +656,11 @@ export const postGetSurfaceIntersectionMutation = (options?: Partial) => [ +export const postGetSampleSurfaceInPointsQueryKey = (options: Options) => [ createQueryKey("postGetSampleSurfaceInPoints", options), ]; -export const postGetSampleSurfaceInPointsOptions = (options: Options) => { +export const postGetSampleSurfaceInPointsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await postGetSampleSurfaceInPoints({ @@ -674,11 +675,11 @@ export const postGetSampleSurfaceInPointsOptions = (options: Options>) => { +export const postGetSampleSurfaceInPointsMutation = (options?: Partial>) => { const mutationOptions: UseMutationOptions< - PostGetSampleSurfaceInPointsResponse, - AxiosError, - Options + PostGetSampleSurfaceInPointsResponse_api, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetSampleSurfaceInPoints({ @@ -692,11 +693,11 @@ export const postGetSampleSurfaceInPointsMutation = (options?: Partial) => [ +export const getDeltaSurfaceDataQueryKey = (options: Options) => [ createQueryKey("getDeltaSurfaceData", options), ]; -export const getDeltaSurfaceDataOptions = (options: Options) => { +export const getDeltaSurfaceDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getDeltaSurfaceData({ @@ -711,11 +712,11 @@ export const getDeltaSurfaceDataOptions = (options: Options) => [ +export const getMisfitSurfaceDataQueryKey = (options: Options) => [ createQueryKey("getMisfitSurfaceData", options), ]; -export const getMisfitSurfaceDataOptions = (options: Options) => { +export const getMisfitSurfaceDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getMisfitSurfaceData({ @@ -730,11 +731,11 @@ export const getMisfitSurfaceDataOptions = (options: Options) => [ +export const getWellboreStratigraphicColumnsQueryKey = (options: Options) => [ createQueryKey("getWellboreStratigraphicColumns", options), ]; -export const getWellboreStratigraphicColumnsOptions = (options: Options) => { +export const getWellboreStratigraphicColumnsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellboreStratigraphicColumns({ @@ -749,11 +750,11 @@ export const getWellboreStratigraphicColumnsOptions = (options: Options) => [ +export const getStratigraphicUnitsQueryKey = (options: Options) => [ createQueryKey("getStratigraphicUnits", options), ]; -export const getStratigraphicUnitsOptions = (options: Options) => { +export const getStratigraphicUnitsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getStratigraphicUnits({ @@ -768,11 +769,11 @@ export const getStratigraphicUnitsOptions = (options: Options) => [ +export const getParameterNamesAndDescriptionQueryKey = (options: Options) => [ createQueryKey("getParameterNamesAndDescription", options), ]; -export const getParameterNamesAndDescriptionOptions = (options: Options) => { +export const getParameterNamesAndDescriptionOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getParameterNamesAndDescription({ @@ -787,9 +788,9 @@ export const getParameterNamesAndDescriptionOptions = (options: Options) => [createQueryKey("getParameter", options)]; +export const getParameterQueryKey = (options: Options) => [createQueryKey("getParameter", options)]; -export const getParameterOptions = (options: Options) => { +export const getParameterOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getParameter({ @@ -804,11 +805,11 @@ export const getParameterOptions = (options: Options) => { }); }; -export const getParametersQueryKey = (options: Options) => [ +export const getParametersQueryKey = (options: Options) => [ createQueryKey("getParameters", options), ]; -export const getParametersOptions = (options: Options) => { +export const getParametersOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getParameters({ @@ -823,11 +824,11 @@ export const getParametersOptions = (options: Options) => { }); }; -export const getIsSensitivityRunQueryKey = (options: Options) => [ +export const getIsSensitivityRunQueryKey = (options: Options) => [ createQueryKey("getIsSensitivityRun", options), ]; -export const getIsSensitivityRunOptions = (options: Options) => { +export const getIsSensitivityRunOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getIsSensitivityRun({ @@ -842,11 +843,11 @@ export const getIsSensitivityRunOptions = (options: Options) => [ +export const getSensitivitiesQueryKey = (options: Options) => [ createQueryKey("getSensitivities", options), ]; -export const getSensitivitiesOptions = (options: Options) => { +export const getSensitivitiesOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getSensitivities({ @@ -861,11 +862,11 @@ export const getSensitivitiesOptions = (options: Options) }); }; -export const getGridModelsInfoQueryKey = (options: Options) => [ +export const getGridModelsInfoQueryKey = (options: Options) => [ createQueryKey("getGridModelsInfo", options), ]; -export const getGridModelsInfoOptions = (options: Options) => { +export const getGridModelsInfoOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getGridModelsInfo({ @@ -880,11 +881,11 @@ export const getGridModelsInfoOptions = (options: Options }); }; -export const getGridSurfaceQueryKey = (options: Options) => [ +export const getGridSurfaceQueryKey = (options: Options) => [ createQueryKey("getGridSurface", options), ]; -export const getGridSurfaceOptions = (options: Options) => { +export const getGridSurfaceOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getGridSurface({ @@ -899,11 +900,11 @@ export const getGridSurfaceOptions = (options: Options) => { }); }; -export const getGridParameterQueryKey = (options: Options) => [ +export const getGridParameterQueryKey = (options: Options) => [ createQueryKey("getGridParameter", options), ]; -export const getGridParameterOptions = (options: Options) => { +export const getGridParameterOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getGridParameter({ @@ -918,11 +919,11 @@ export const getGridParameterOptions = (options: Options) }); }; -export const postGetPolylineIntersectionQueryKey = (options: Options) => [ +export const postGetPolylineIntersectionQueryKey = (options: Options) => [ createQueryKey("postGetPolylineIntersection", options), ]; -export const postGetPolylineIntersectionOptions = (options: Options) => { +export const postGetPolylineIntersectionOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await postGetPolylineIntersection({ @@ -937,11 +938,11 @@ export const postGetPolylineIntersectionOptions = (options: Options>) => { +export const postGetPolylineIntersectionMutation = (options?: Partial>) => { const mutationOptions: UseMutationOptions< - PostGetPolylineIntersectionResponse, - AxiosError, - Options + PostGetPolylineIntersectionResponse_api, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetPolylineIntersection({ @@ -955,11 +956,11 @@ export const postGetPolylineIntersectionMutation = (options?: Partial) => [ +export const getRealizationFlowNetworkQueryKey = (options: Options) => [ createQueryKey("getRealizationFlowNetwork", options), ]; -export const getRealizationFlowNetworkOptions = (options: Options) => { +export const getRealizationFlowNetworkOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getRealizationFlowNetwork({ @@ -974,9 +975,9 @@ export const getRealizationFlowNetworkOptions = (options: Options) => [createQueryKey("getTableData", options)]; +export const getTableDataQueryKey = (options: Options) => [createQueryKey("getTableData", options)]; -export const getTableDataOptions = (options: Options) => { +export const getTableDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getTableData({ @@ -991,11 +992,11 @@ export const getTableDataOptions = (options: Options) => { }); }; -export const getWellCompletionsDataQueryKey = (options: Options) => [ +export const getWellCompletionsDataQueryKey = (options: Options) => [ createQueryKey("getWellCompletionsData", options), ]; -export const getWellCompletionsDataOptions = (options: Options) => { +export const getWellCompletionsDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellCompletionsData({ @@ -1010,11 +1011,11 @@ export const getWellCompletionsDataOptions = (options: Options) => [ +export const getDrilledWellboreHeadersQueryKey = (options: Options) => [ createQueryKey("getDrilledWellboreHeaders", options), ]; -export const getDrilledWellboreHeadersOptions = (options: Options) => { +export const getDrilledWellboreHeadersOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getDrilledWellboreHeaders({ @@ -1029,11 +1030,11 @@ export const getDrilledWellboreHeadersOptions = (options: Options) => [ +export const getWellTrajectoriesQueryKey = (options: Options) => [ createQueryKey("getWellTrajectories", options), ]; -export const getWellTrajectoriesOptions = (options: Options) => { +export const getWellTrajectoriesOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellTrajectories({ @@ -1048,11 +1049,11 @@ export const getWellTrajectoriesOptions = (options: Options) => [ +export const getWellborePickIdentifiersQueryKey = (options: Options) => [ createQueryKey("getWellborePickIdentifiers", options), ]; -export const getWellborePickIdentifiersOptions = (options: Options) => { +export const getWellborePickIdentifiersOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellborePickIdentifiers({ @@ -1067,11 +1068,11 @@ export const getWellborePickIdentifiersOptions = (options: Options) => [ +export const getWellborePicksForPickIdentifierQueryKey = (options: Options) => [ createQueryKey("getWellborePicksForPickIdentifier", options), ]; -export const getWellborePicksForPickIdentifierOptions = (options: Options) => { +export const getWellborePicksForPickIdentifierOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellborePicksForPickIdentifier({ @@ -1086,11 +1087,11 @@ export const getWellborePicksForPickIdentifierOptions = (options: Options) => [ +export const getWellborePicksForWellboreQueryKey = (options: Options) => [ createQueryKey("getWellborePicksForWellbore", options), ]; -export const getWellborePicksForWellboreOptions = (options: Options) => { +export const getWellborePicksForWellboreOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellborePicksForWellbore({ @@ -1105,11 +1106,11 @@ export const getWellborePicksForWellboreOptions = (options: Options) => [ +export const getWellborePicksInStratColumnQueryKey = (options: Options) => [ createQueryKey("getWellborePicksInStratColumn", options), ]; -export const getWellborePicksInStratColumnOptions = (options: Options) => { +export const getWellborePicksInStratColumnOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellborePicksInStratColumn({ @@ -1124,11 +1125,11 @@ export const getWellborePicksInStratColumnOptions = (options: Options) => [ +export const getWellboreCompletionsQueryKey = (options: Options) => [ createQueryKey("getWellboreCompletions", options), ]; -export const getWellboreCompletionsOptions = (options: Options) => { +export const getWellboreCompletionsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellboreCompletions({ @@ -1143,11 +1144,11 @@ export const getWellboreCompletionsOptions = (options: Options) => [ +export const getWellboreCasingsQueryKey = (options: Options) => [ createQueryKey("getWellboreCasings", options), ]; -export const getWellboreCasingsOptions = (options: Options) => { +export const getWellboreCasingsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellboreCasings({ @@ -1162,11 +1163,11 @@ export const getWellboreCasingsOptions = (options: Options) => [ +export const getWellborePerforationsQueryKey = (options: Options) => [ createQueryKey("getWellborePerforations", options), ]; -export const getWellborePerforationsOptions = (options: Options) => { +export const getWellborePerforationsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellborePerforations({ @@ -1181,11 +1182,11 @@ export const getWellborePerforationsOptions = (options: Options) => [ +export const getWellboreLogCurveHeadersQueryKey = (options: Options) => [ createQueryKey("getWellboreLogCurveHeaders", options), ]; -export const getWellboreLogCurveHeadersOptions = (options: Options) => { +export const getWellboreLogCurveHeadersOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getWellboreLogCurveHeaders({ @@ -1200,11 +1201,11 @@ export const getWellboreLogCurveHeadersOptions = (options: Options) => [ +export const getLogCurveDataQueryKey = (options: Options) => [ createQueryKey("getLogCurveData", options), ]; -export const getLogCurveDataOptions = (options: Options) => { +export const getLogCurveDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getLogCurveData({ @@ -1219,11 +1220,11 @@ export const getLogCurveDataOptions = (options: Options) => }); }; -export const getSeismicCubeMetaListQueryKey = (options: Options) => [ +export const getSeismicCubeMetaListQueryKey = (options: Options) => [ createQueryKey("getSeismicCubeMetaList", options), ]; -export const getSeismicCubeMetaListOptions = (options: Options) => { +export const getSeismicCubeMetaListOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getSeismicCubeMetaList({ @@ -1238,11 +1239,11 @@ export const getSeismicCubeMetaListOptions = (options: Options) => [ +export const getInlineSliceQueryKey = (options: Options) => [ createQueryKey("getInlineSlice", options), ]; -export const getInlineSliceOptions = (options: Options) => { +export const getInlineSliceOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getInlineSlice({ @@ -1257,11 +1258,11 @@ export const getInlineSliceOptions = (options: Options) => { }); }; -export const getCrosslineSliceQueryKey = (options: Options) => [ +export const getCrosslineSliceQueryKey = (options: Options) => [ createQueryKey("getCrosslineSlice", options), ]; -export const getCrosslineSliceOptions = (options: Options) => { +export const getCrosslineSliceOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getCrosslineSlice({ @@ -1276,11 +1277,11 @@ export const getCrosslineSliceOptions = (options: Options }); }; -export const getDepthSliceQueryKey = (options: Options) => [ +export const getDepthSliceQueryKey = (options: Options) => [ createQueryKey("getDepthSlice", options), ]; -export const getDepthSliceOptions = (options: Options) => { +export const getDepthSliceOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getDepthSlice({ @@ -1295,11 +1296,11 @@ export const getDepthSliceOptions = (options: Options) => { }); }; -export const postGetSeismicFenceQueryKey = (options: Options) => [ +export const postGetSeismicFenceQueryKey = (options: Options) => [ createQueryKey("postGetSeismicFence", options), ]; -export const postGetSeismicFenceOptions = (options: Options) => { +export const postGetSeismicFenceOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await postGetSeismicFence({ @@ -1314,11 +1315,11 @@ export const postGetSeismicFenceOptions = (options: Options>) => { +export const postGetSeismicFenceMutation = (options?: Partial>) => { const mutationOptions: UseMutationOptions< - PostGetSeismicFenceResponse, - AxiosError, - Options + PostGetSeismicFenceResponse_api, + AxiosError, + Options > = { mutationFn: async (localOptions) => { const { data } = await postGetSeismicFence({ @@ -1332,11 +1333,11 @@ export const postGetSeismicFenceMutation = (options?: Partial) => [ +export const getPolygonsDirectoryQueryKey = (options: Options) => [ createQueryKey("getPolygonsDirectory", options), ]; -export const getPolygonsDirectoryOptions = (options: Options) => { +export const getPolygonsDirectoryOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getPolygonsDirectory({ @@ -1351,11 +1352,11 @@ export const getPolygonsDirectoryOptions = (options: Options) => [ +export const getPolygonsDataQueryKey = (options: Options) => [ createQueryKey("getPolygonsData", options), ]; -export const getPolygonsDataOptions = (options: Options) => { +export const getPolygonsDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getPolygonsData({ @@ -1370,9 +1371,9 @@ export const getPolygonsDataOptions = (options: Options) => }); }; -export const getUserPhotoQueryKey = (options: Options) => [createQueryKey("getUserPhoto", options)]; +export const getUserPhotoQueryKey = (options: Options) => [createQueryKey("getUserPhoto", options)]; -export const getUserPhotoOptions = (options: Options) => { +export const getUserPhotoOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getUserPhoto({ @@ -1387,11 +1388,11 @@ export const getUserPhotoOptions = (options: Options) => { }); }; -export const getObservationsQueryKey = (options: Options) => [ +export const getObservationsQueryKey = (options: Options) => [ createQueryKey("getObservations", options), ]; -export const getObservationsOptions = (options: Options) => { +export const getObservationsOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getObservations({ @@ -1406,11 +1407,11 @@ export const getObservationsOptions = (options: Options) => }); }; -export const getTableDefinitionQueryKey = (options: Options) => [ +export const getTableDefinitionQueryKey = (options: Options) => [ createQueryKey("getTableDefinition", options), ]; -export const getTableDefinitionOptions = (options: Options) => { +export const getTableDefinitionOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getTableDefinition({ @@ -1425,11 +1426,11 @@ export const getTableDefinitionOptions = (options: Options) => [ +export const getRealizationDataQueryKey = (options: Options) => [ createQueryKey("getRealizationData", options), ]; -export const getRealizationDataOptions = (options: Options) => { +export const getRealizationDataOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getRealizationData({ @@ -1444,11 +1445,11 @@ export const getRealizationDataOptions = (options: Options) => [ +export const getVfpTableNamesQueryKey = (options: Options) => [ createQueryKey("getVfpTableNames", options), ]; -export const getVfpTableNamesOptions = (options: Options) => { +export const getVfpTableNamesOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getVfpTableNames({ @@ -1463,9 +1464,9 @@ export const getVfpTableNamesOptions = (options: Options) }); }; -export const getVfpTableQueryKey = (options: Options) => [createQueryKey("getVfpTable", options)]; +export const getVfpTableQueryKey = (options: Options) => [createQueryKey("getVfpTable", options)]; -export const getVfpTableOptions = (options: Options) => { +export const getVfpTableOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getVfpTable({ @@ -1480,9 +1481,9 @@ export const getVfpTableOptions = (options: Options) => { }); }; -export const loginRouteQueryKey = (options?: Options) => [createQueryKey("loginRoute", options)]; +export const loginRouteQueryKey = (options?: Options) => [createQueryKey("loginRoute", options)]; -export const loginRouteOptions = (options?: Options) => { +export const loginRouteOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await loginRoute({ @@ -1497,11 +1498,11 @@ export const loginRouteOptions = (options?: Options) => { }); }; -export const authorizedCallbackRouteQueryKey = (options?: Options) => [ +export const authorizedCallbackRouteQueryKey = (options?: Options) => [ createQueryKey("authorizedCallbackRoute", options), ]; -export const authorizedCallbackRouteOptions = (options?: Options) => { +export const authorizedCallbackRouteOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await authorizedCallbackRoute({ @@ -1516,9 +1517,9 @@ export const authorizedCallbackRouteOptions = (options?: Options) => [createQueryKey("getAlive", options)]; +export const getAliveQueryKey = (options?: Options) => [createQueryKey("getAlive", options)]; -export const getAliveOptions = (options?: Options) => { +export const getAliveOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getAlive({ @@ -1533,11 +1534,11 @@ export const getAliveOptions = (options?: Options) => { }); }; -export const getAliveProtectedQueryKey = (options?: Options) => [ +export const getAliveProtectedQueryKey = (options?: Options) => [ createQueryKey("getAliveProtected", options), ]; -export const getAliveProtectedOptions = (options?: Options) => { +export const getAliveProtectedOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getAliveProtected({ @@ -1552,9 +1553,9 @@ export const getAliveProtectedOptions = (options?: Options) => [createQueryKey("postLogout", options)]; +export const postLogoutQueryKey = (options?: Options) => [createQueryKey("postLogout", options)]; -export const postLogoutOptions = (options?: Options) => { +export const postLogoutOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await postLogout({ @@ -1569,8 +1570,8 @@ export const postLogoutOptions = (options?: Options) => { }); }; -export const postLogoutMutation = (options?: Partial>) => { - const mutationOptions: UseMutationOptions, Options> = { +export const postLogoutMutation = (options?: Partial>) => { + const mutationOptions: UseMutationOptions, Options> = { mutationFn: async (localOptions) => { const { data } = await postLogout({ ...options, @@ -1583,11 +1584,11 @@ export const postLogoutMutation = (options?: Partial>) = return mutationOptions; }; -export const getLoggedInUserQueryKey = (options?: Options) => [ +export const getLoggedInUserQueryKey = (options?: Options) => [ createQueryKey("getLoggedInUser", options), ]; -export const getLoggedInUserOptions = (options?: Options) => { +export const getLoggedInUserOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await getLoggedInUser({ @@ -1602,9 +1603,9 @@ export const getLoggedInUserOptions = (options?: Options) = }); }; -export const rootQueryKey = (options?: Options) => [createQueryKey("root", options)]; +export const rootQueryKey = (options?: Options) => [createQueryKey("root", options)]; -export const rootOptions = (options?: Options) => { +export const rootOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await root({ diff --git a/frontend/src/api/autogen/index.ts b/frontend/src/api/autogen/index.ts index da8707936..bcf1aa9cc 100644 --- a/frontend/src/api/autogen/index.ts +++ b/frontend/src/api/autogen/index.ts @@ -1,3 +1,5 @@ // This file is auto-generated by @hey-api/openapi-ts export * from "./types.gen"; export * from "./sdk.gen"; + +export * from './@tanstack/react-query.gen'; diff --git a/frontend/src/api/autogen/sdk.gen.ts b/frontend/src/api/autogen/sdk.gen.ts index df38a1c98..984ab25f5 100644 --- a/frontend/src/api/autogen/sdk.gen.ts +++ b/frontend/src/api/autogen/sdk.gen.ts @@ -1,206 +1,207 @@ // This file is auto-generated by @hey-api/openapi-ts import { createClient, createConfig, type Options } from "@hey-api/client-axios"; + import type { - GetFieldsData, - GetFieldsResponse, - GetCasesData, - GetCasesResponse, - GetCasesError, - GetEnsemblesData, - GetEnsemblesResponse, - GetEnsemblesError, - GetEnsembleDetailsData, - GetEnsembleDetailsResponse, - GetEnsembleDetailsError, - GetVectorListData, - GetVectorListResponse, - GetVectorListError, - GetDeltaEnsembleVectorListData, - GetDeltaEnsembleVectorListResponse, - GetDeltaEnsembleVectorListError, - GetRealizationsVectorDataData, - GetRealizationsVectorDataResponse, - GetRealizationsVectorDataError, - GetDeltaEnsembleRealizationsVectorDataData, - GetDeltaEnsembleRealizationsVectorDataResponse, - GetDeltaEnsembleRealizationsVectorDataError, - GetTimestampsListData, - GetTimestampsListResponse, - GetTimestampsListError, - GetHistoricalVectorDataData, - GetHistoricalVectorDataResponse, - GetHistoricalVectorDataError, - GetStatisticalVectorDataData, - GetStatisticalVectorDataResponse, - GetStatisticalVectorDataError, - GetDeltaEnsembleStatisticalVectorDataData, - GetDeltaEnsembleStatisticalVectorDataResponse, - GetDeltaEnsembleStatisticalVectorDataError, - GetStatisticalVectorDataPerSensitivityData, - GetStatisticalVectorDataPerSensitivityResponse, - GetStatisticalVectorDataPerSensitivityError, - GetRealizationVectorAtTimestampData, - GetRealizationVectorAtTimestampResponse, - GetRealizationVectorAtTimestampError, - GetTableDefinitionsData, - GetTableDefinitionsResponse, - GetTableDefinitionsError, - PostGetAggregatedPerRealizationTableDataData, - PostGetAggregatedPerRealizationTableDataResponse, - PostGetAggregatedPerRealizationTableDataError, - PostGetAggregatedStatisticalTableDataData, - PostGetAggregatedStatisticalTableDataResponse, - PostGetAggregatedStatisticalTableDataError, - GetRealizationSurfacesMetadataData, - GetRealizationSurfacesMetadataResponse, - GetRealizationSurfacesMetadataError, - GetObservedSurfacesMetadataData, - GetObservedSurfacesMetadataResponse, - GetObservedSurfacesMetadataError, - GetSurfaceDataData, - GetSurfaceDataResponse, - GetSurfaceDataError, - PostGetSurfaceIntersectionData, - PostGetSurfaceIntersectionResponse, - PostGetSurfaceIntersectionError, - PostGetSampleSurfaceInPointsData, - PostGetSampleSurfaceInPointsResponse, - PostGetSampleSurfaceInPointsError, - GetDeltaSurfaceDataData, - GetDeltaSurfaceDataResponse, - GetDeltaSurfaceDataError, - GetMisfitSurfaceDataData, - GetMisfitSurfaceDataResponse, - GetMisfitSurfaceDataError, - GetWellboreStratigraphicColumnsData, - GetWellboreStratigraphicColumnsResponse, - GetWellboreStratigraphicColumnsError, - GetStratigraphicUnitsData, - GetStratigraphicUnitsResponse, - GetStratigraphicUnitsError, - GetParameterNamesAndDescriptionData, - GetParameterNamesAndDescriptionResponse, - GetParameterNamesAndDescriptionError, - GetParameterData, - GetParameterResponse, - GetParameterError, - GetParametersData, - GetParametersResponse, - GetParametersError, - GetIsSensitivityRunData, - GetIsSensitivityRunResponse, - GetIsSensitivityRunError, - GetSensitivitiesData, - GetSensitivitiesResponse, - GetSensitivitiesError, - GetGridModelsInfoData, - GetGridModelsInfoResponse, - GetGridModelsInfoError, - GetGridSurfaceData, - GetGridSurfaceResponse, - GetGridSurfaceError, - GetGridParameterData, - GetGridParameterResponse, - GetGridParameterError, - PostGetPolylineIntersectionData, - PostGetPolylineIntersectionResponse, - PostGetPolylineIntersectionError, - GetRealizationFlowNetworkData, - GetRealizationFlowNetworkResponse, - GetRealizationFlowNetworkError, - GetTableDataData, - GetTableDataResponse, - GetTableDataError, - GetWellCompletionsDataData, - GetWellCompletionsDataResponse, - GetWellCompletionsDataError, - GetDrilledWellboreHeadersData, - GetDrilledWellboreHeadersResponse, - GetDrilledWellboreHeadersError, - GetWellTrajectoriesData, - GetWellTrajectoriesResponse, - GetWellTrajectoriesError, - GetWellborePickIdentifiersData, - GetWellborePickIdentifiersResponse, - GetWellborePickIdentifiersError, - GetWellborePicksForPickIdentifierData, - GetWellborePicksForPickIdentifierResponse, - GetWellborePicksForPickIdentifierError, - GetWellborePicksForWellboreData, - GetWellborePicksForWellboreResponse, - GetWellborePicksForWellboreError, - GetWellborePicksInStratColumnData, - GetWellborePicksInStratColumnResponse, - GetWellborePicksInStratColumnError, - GetWellboreCompletionsData, - GetWellboreCompletionsResponse, - GetWellboreCompletionsError, - GetWellboreCasingsData, - GetWellboreCasingsResponse, - GetWellboreCasingsError, - GetWellborePerforationsData, - GetWellborePerforationsResponse, - GetWellborePerforationsError, - GetWellboreLogCurveHeadersData, - GetWellboreLogCurveHeadersResponse, - GetWellboreLogCurveHeadersError, - GetLogCurveDataData, - GetLogCurveDataResponse, - GetLogCurveDataError, - GetSeismicCubeMetaListData, - GetSeismicCubeMetaListResponse, - GetSeismicCubeMetaListError, - GetInlineSliceData, - GetInlineSliceResponse, - GetInlineSliceError, - GetCrosslineSliceData, - GetCrosslineSliceResponse, - GetCrosslineSliceError, - GetDepthSliceData, - GetDepthSliceResponse, - GetDepthSliceError, - PostGetSeismicFenceData, - PostGetSeismicFenceResponse, - PostGetSeismicFenceError, - GetPolygonsDirectoryData, - GetPolygonsDirectoryResponse, - GetPolygonsDirectoryError, - GetPolygonsDataData, - GetPolygonsDataResponse, - GetPolygonsDataError, - GetUserPhotoData, - GetUserPhotoResponse, - GetUserPhotoError, - GetObservationsData, - GetObservationsResponse, - GetObservationsError, - GetTableDefinitionData, - GetTableDefinitionResponse, - GetTableDefinitionError, - GetRealizationDataData, - GetRealizationDataResponse, - GetRealizationDataError, - GetVfpTableNamesData, - GetVfpTableNamesResponse, - GetVfpTableNamesError, - GetVfpTableData, - GetVfpTableResponse, - GetVfpTableError, - LoginRouteData, - LoginRouteError, - AuthorizedCallbackRouteData, - GetAliveData, - GetAliveResponse, - GetAliveProtectedData, - GetAliveProtectedResponse, - PostLogoutData, - PostLogoutResponse, - GetLoggedInUserData, - GetLoggedInUserResponse, - GetLoggedInUserError, - RootData, - RootResponse, + GetFieldsData_api, + GetFieldsResponse_api, + GetCasesData_api, + GetCasesResponse_api, + GetCasesError_api, + GetEnsemblesData_api, + GetEnsemblesResponse_api, + GetEnsemblesError_api, + GetEnsembleDetailsData_api, + GetEnsembleDetailsResponse_api, + GetEnsembleDetailsError_api, + GetVectorListData_api, + GetVectorListResponse_api, + GetVectorListError_api, + GetDeltaEnsembleVectorListData_api, + GetDeltaEnsembleVectorListResponse_api, + GetDeltaEnsembleVectorListError_api, + GetRealizationsVectorDataData_api, + GetRealizationsVectorDataResponse_api, + GetRealizationsVectorDataError_api, + GetDeltaEnsembleRealizationsVectorDataData_api, + GetDeltaEnsembleRealizationsVectorDataResponse_api, + GetDeltaEnsembleRealizationsVectorDataError_api, + GetTimestampsListData_api, + GetTimestampsListResponse_api, + GetTimestampsListError_api, + GetHistoricalVectorDataData_api, + GetHistoricalVectorDataResponse_api, + GetHistoricalVectorDataError_api, + GetStatisticalVectorDataData_api, + GetStatisticalVectorDataResponse_api, + GetStatisticalVectorDataError_api, + GetDeltaEnsembleStatisticalVectorDataData_api, + GetDeltaEnsembleStatisticalVectorDataResponse_api, + GetDeltaEnsembleStatisticalVectorDataError_api, + GetStatisticalVectorDataPerSensitivityData_api, + GetStatisticalVectorDataPerSensitivityResponse_api, + GetStatisticalVectorDataPerSensitivityError_api, + GetRealizationVectorAtTimestampData_api, + GetRealizationVectorAtTimestampResponse_api, + GetRealizationVectorAtTimestampError_api, + GetTableDefinitionsData_api, + GetTableDefinitionsResponse_api, + GetTableDefinitionsError_api, + PostGetAggregatedPerRealizationTableDataData_api, + PostGetAggregatedPerRealizationTableDataResponse_api, + PostGetAggregatedPerRealizationTableDataError_api, + PostGetAggregatedStatisticalTableDataData_api, + PostGetAggregatedStatisticalTableDataResponse_api, + PostGetAggregatedStatisticalTableDataError_api, + GetRealizationSurfacesMetadataData_api, + GetRealizationSurfacesMetadataResponse_api, + GetRealizationSurfacesMetadataError_api, + GetObservedSurfacesMetadataData_api, + GetObservedSurfacesMetadataResponse_api, + GetObservedSurfacesMetadataError_api, + GetSurfaceDataData_api, + GetSurfaceDataResponse_api, + GetSurfaceDataError_api, + PostGetSurfaceIntersectionData_api, + PostGetSurfaceIntersectionResponse_api, + PostGetSurfaceIntersectionError_api, + PostGetSampleSurfaceInPointsData_api, + PostGetSampleSurfaceInPointsResponse_api, + PostGetSampleSurfaceInPointsError_api, + GetDeltaSurfaceDataData_api, + GetDeltaSurfaceDataResponse_api, + GetDeltaSurfaceDataError_api, + GetMisfitSurfaceDataData_api, + GetMisfitSurfaceDataResponse_api, + GetMisfitSurfaceDataError_api, + GetWellboreStratigraphicColumnsData_api, + GetWellboreStratigraphicColumnsResponse_api, + GetWellboreStratigraphicColumnsError_api, + GetStratigraphicUnitsData_api, + GetStratigraphicUnitsResponse_api, + GetStratigraphicUnitsError_api, + GetParameterNamesAndDescriptionData_api, + GetParameterNamesAndDescriptionResponse_api, + GetParameterNamesAndDescriptionError_api, + GetParameterData_api, + GetParameterResponse_api, + GetParameterError_api, + GetParametersData_api, + GetParametersResponse_api, + GetParametersError_api, + GetIsSensitivityRunData_api, + GetIsSensitivityRunResponse_api, + GetIsSensitivityRunError_api, + GetSensitivitiesData_api, + GetSensitivitiesResponse_api, + GetSensitivitiesError_api, + GetGridModelsInfoData_api, + GetGridModelsInfoResponse_api, + GetGridModelsInfoError_api, + GetGridSurfaceData_api, + GetGridSurfaceResponse_api, + GetGridSurfaceError_api, + GetGridParameterData_api, + GetGridParameterResponse_api, + GetGridParameterError_api, + PostGetPolylineIntersectionData_api, + PostGetPolylineIntersectionResponse_api, + PostGetPolylineIntersectionError_api, + GetRealizationFlowNetworkData_api, + GetRealizationFlowNetworkResponse_api, + GetRealizationFlowNetworkError_api, + GetTableDataData_api, + GetTableDataResponse_api, + GetTableDataError_api, + GetWellCompletionsDataData_api, + GetWellCompletionsDataResponse_api, + GetWellCompletionsDataError_api, + GetDrilledWellboreHeadersData_api, + GetDrilledWellboreHeadersResponse_api, + GetDrilledWellboreHeadersError_api, + GetWellTrajectoriesData_api, + GetWellTrajectoriesResponse_api, + GetWellTrajectoriesError_api, + GetWellborePickIdentifiersData_api, + GetWellborePickIdentifiersResponse_api, + GetWellborePickIdentifiersError_api, + GetWellborePicksForPickIdentifierData_api, + GetWellborePicksForPickIdentifierResponse_api, + GetWellborePicksForPickIdentifierError_api, + GetWellborePicksForWellboreData_api, + GetWellborePicksForWellboreResponse_api, + GetWellborePicksForWellboreError_api, + GetWellborePicksInStratColumnData_api, + GetWellborePicksInStratColumnResponse_api, + GetWellborePicksInStratColumnError_api, + GetWellboreCompletionsData_api, + GetWellboreCompletionsResponse_api, + GetWellboreCompletionsError_api, + GetWellboreCasingsData_api, + GetWellboreCasingsResponse_api, + GetWellboreCasingsError_api, + GetWellborePerforationsData_api, + GetWellborePerforationsResponse_api, + GetWellborePerforationsError_api, + GetWellboreLogCurveHeadersData_api, + GetWellboreLogCurveHeadersResponse_api, + GetWellboreLogCurveHeadersError_api, + GetLogCurveDataData_api, + GetLogCurveDataResponse_api, + GetLogCurveDataError_api, + GetSeismicCubeMetaListData_api, + GetSeismicCubeMetaListResponse_api, + GetSeismicCubeMetaListError_api, + GetInlineSliceData_api, + GetInlineSliceResponse_api, + GetInlineSliceError_api, + GetCrosslineSliceData_api, + GetCrosslineSliceResponse_api, + GetCrosslineSliceError_api, + GetDepthSliceData_api, + GetDepthSliceResponse_api, + GetDepthSliceError_api, + PostGetSeismicFenceData_api, + PostGetSeismicFenceResponse_api, + PostGetSeismicFenceError_api, + GetPolygonsDirectoryData_api, + GetPolygonsDirectoryResponse_api, + GetPolygonsDirectoryError_api, + GetPolygonsDataData_api, + GetPolygonsDataResponse_api, + GetPolygonsDataError_api, + GetUserPhotoData_api, + GetUserPhotoResponse_api, + GetUserPhotoError_api, + GetObservationsData_api, + GetObservationsResponse_api, + GetObservationsError_api, + GetTableDefinitionData_api, + GetTableDefinitionResponse_api, + GetTableDefinitionError_api, + GetRealizationDataData_api, + GetRealizationDataResponse_api, + GetRealizationDataError_api, + GetVfpTableNamesData_api, + GetVfpTableNamesResponse_api, + GetVfpTableNamesError_api, + GetVfpTableData_api, + GetVfpTableResponse_api, + GetVfpTableError_api, + LoginRouteData_api, + LoginRouteError_api, + AuthorizedCallbackRouteData_api, + GetAliveData_api, + GetAliveResponse_api, + GetAliveProtectedData_api, + GetAliveProtectedResponse_api, + PostLogoutData_api, + PostLogoutResponse_api, + GetLoggedInUserData_api, + GetLoggedInUserResponse_api, + GetLoggedInUserError_api, + RootData_api, + RootResponse_api, } from "./types.gen"; export const client = createClient(createConfig()); @@ -209,8 +210,8 @@ export const client = createClient(createConfig()); * Get Fields * Get list of fields */ -export const getFields = (options?: Options) => { - return (options?.client ?? client).get({ +export const getFields = (options?: Options) => { + return (options?.client ?? client).get({ ...options, url: "/fields", }); @@ -220,8 +221,8 @@ export const getFields = (options?: Option * Get Cases * Get list of cases for specified field */ -export const getCases = (options: Options) => { - return (options?.client ?? client).get({ +export const getCases = (options: Options) => { + return (options?.client ?? client).get({ ...options, url: "/cases", }); @@ -232,9 +233,9 @@ export const getCases = (options: Options< * Get list of ensembles for a case */ export const getEnsembles = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/cases/{case_uuid}/ensembles", }); @@ -245,9 +246,9 @@ export const getEnsembles = ( * Get more detailed information for an ensemble */ export const getEnsembleDetails = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/cases/{case_uuid}/ensembles/{ensemble_name}", }); @@ -260,9 +261,9 @@ export const getEnsembleDetails = ( * Optionally include derived vectors. */ export const getVectorList = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/timeseries/vector_list/", }); @@ -277,11 +278,11 @@ export const getVectorList = ( * delta_ensemble = comparison_ensemble - reference_ensemble */ export const getDeltaEnsembleVectorList = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetDeltaEnsembleVectorListResponse, - GetDeltaEnsembleVectorListError, + GetDeltaEnsembleVectorListResponse_api, + GetDeltaEnsembleVectorListError_api, ThrowOnError >({ ...options, @@ -294,11 +295,11 @@ export const getDeltaEnsembleVectorList = * Get vector data per realization */ export const getRealizationsVectorData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetRealizationsVectorDataResponse, - GetRealizationsVectorDataError, + GetRealizationsVectorDataResponse_api, + GetRealizationsVectorDataError_api, ThrowOnError >({ ...options, @@ -315,11 +316,11 @@ export const getRealizationsVectorData = ( * delta_ensemble = comparison_ensemble - reference_ensemble */ export const getDeltaEnsembleRealizationsVectorData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetDeltaEnsembleRealizationsVectorDataResponse, - GetDeltaEnsembleRealizationsVectorDataError, + GetDeltaEnsembleRealizationsVectorDataResponse_api, + GetDeltaEnsembleRealizationsVectorDataError_api, ThrowOnError >({ ...options, @@ -337,9 +338,9 @@ export const getDeltaEnsembleRealizationsVectorData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/timeseries/timestamps_list/", }); @@ -349,9 +350,9 @@ export const getTimestampsList = ( * Get Historical Vector Data */ export const getHistoricalVectorData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get( + return (options?.client ?? client).get( { ...options, url: "/timeseries/historical_vector_data/", @@ -364,11 +365,11 @@ export const getHistoricalVectorData = ( * Get statistical vector data for an ensemble */ export const getStatisticalVectorData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetStatisticalVectorDataResponse, - GetStatisticalVectorDataError, + GetStatisticalVectorDataResponse_api, + GetStatisticalVectorDataError_api, ThrowOnError >({ ...options, @@ -385,11 +386,11 @@ export const getStatisticalVectorData = ( * delta_ensemble = comparison_ensemble - reference_ensemble */ export const getDeltaEnsembleStatisticalVectorData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetDeltaEnsembleStatisticalVectorDataResponse, - GetDeltaEnsembleStatisticalVectorDataError, + GetDeltaEnsembleStatisticalVectorDataResponse_api, + GetDeltaEnsembleStatisticalVectorDataError_api, ThrowOnError >({ ...options, @@ -402,11 +403,11 @@ export const getDeltaEnsembleStatisticalVectorData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetStatisticalVectorDataPerSensitivityResponse, - GetStatisticalVectorDataPerSensitivityError, + GetStatisticalVectorDataPerSensitivityResponse_api, + GetStatisticalVectorDataPerSensitivityError_api, ThrowOnError >({ ...options, @@ -418,11 +419,11 @@ export const getStatisticalVectorDataPerSensitivity = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetRealizationVectorAtTimestampResponse, - GetRealizationVectorAtTimestampError, + GetRealizationVectorAtTimestampResponse_api, + GetRealizationVectorAtTimestampError_api, ThrowOnError >({ ...options, @@ -435,9 +436,9 @@ export const getRealizationVectorAtTimestamp = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/inplace_volumetrics/table_definitions/", }); @@ -451,11 +452,11 @@ export const getTableDefinitions = ( * As the endpoint is post, the identifiers with values object is kept for convenience. */ export const postGetAggregatedPerRealizationTableData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).post< - PostGetAggregatedPerRealizationTableDataResponse, - PostGetAggregatedPerRealizationTableDataError, + PostGetAggregatedPerRealizationTableDataResponse_api, + PostGetAggregatedPerRealizationTableDataError_api, ThrowOnError >({ ...options, @@ -475,11 +476,11 @@ export const postGetAggregatedPerRealizationTableData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).post< - PostGetAggregatedStatisticalTableDataResponse, - PostGetAggregatedStatisticalTableDataError, + PostGetAggregatedStatisticalTableDataResponse_api, + PostGetAggregatedStatisticalTableDataError_api, ThrowOnError >({ ...options, @@ -496,11 +497,11 @@ export const postGetAggregatedStatisticalTableData = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetRealizationSurfacesMetadataResponse, - GetRealizationSurfacesMetadataError, + GetRealizationSurfacesMetadataResponse_api, + GetRealizationSurfacesMetadataError_api, ThrowOnError >({ ...options, @@ -513,11 +514,11 @@ export const getRealizationSurfacesMetadata = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetObservedSurfacesMetadataResponse, - GetObservedSurfacesMetadataError, + GetObservedSurfacesMetadataResponse_api, + GetObservedSurfacesMetadataError_api, ThrowOnError >({ ...options, @@ -550,9 +551,9 @@ export const getObservedSurfacesMetadata = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/surface/surface_data", }); @@ -566,11 +567,11 @@ export const getSurfaceData = ( * and cumulative lengths, the accumulated length at each z-point in the array. */ export const postGetSurfaceIntersection = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).post< - PostGetSurfaceIntersectionResponse, - PostGetSurfaceIntersectionError, + PostGetSurfaceIntersectionResponse_api, + PostGetSurfaceIntersectionError_api, ThrowOnError >({ ...options, @@ -586,11 +587,11 @@ export const postGetSurfaceIntersection = * Post Get Sample Surface In Points */ export const postGetSampleSurfaceInPoints = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).post< - PostGetSampleSurfaceInPointsResponse, - PostGetSampleSurfaceInPointsError, + PostGetSampleSurfaceInPointsResponse_api, + PostGetSampleSurfaceInPointsError_api, ThrowOnError >({ ...options, @@ -606,9 +607,9 @@ export const postGetSampleSurfaceInPoints = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/surface/delta_surface_data", }); @@ -618,9 +619,9 @@ export const getDeltaSurfaceData = ( * Get Misfit Surface Data */ export const getMisfitSurfaceData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/surface/misfit_surface_data", }); @@ -630,11 +631,11 @@ export const getMisfitSurfaceData = ( * Get Wellbore Stratigraphic Columns */ export const getWellboreStratigraphicColumns = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellboreStratigraphicColumnsResponse, - GetWellboreStratigraphicColumnsError, + GetWellboreStratigraphicColumnsResponse_api, + GetWellboreStratigraphicColumnsError_api, ThrowOnError >({ ...options, @@ -646,9 +647,9 @@ export const getWellboreStratigraphicColumns = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/surface/stratigraphic_units", }); @@ -659,11 +660,11 @@ export const getStratigraphicUnits = ( * Retrieve parameter names and description for an ensemble */ export const getParameterNamesAndDescription = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetParameterNamesAndDescriptionResponse, - GetParameterNamesAndDescriptionError, + GetParameterNamesAndDescriptionResponse_api, + GetParameterNamesAndDescriptionError_api, ThrowOnError >({ ...options, @@ -676,9 +677,9 @@ export const getParameterNamesAndDescription = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/parameters/parameter/", }); @@ -688,9 +689,9 @@ export const getParameter = ( * Get Parameters */ export const getParameters = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/parameters/parameters/", }); @@ -701,9 +702,9 @@ export const getParameters = ( * Check if a given Sumo ensemble is a sensitivity run */ export const getIsSensitivityRun = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/parameters/is_sensitivity_run/", }); @@ -714,9 +715,9 @@ export const getIsSensitivityRun = ( * Get sensitivities in a given Sumo ensemble */ export const getSensitivities = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/parameters/sensitivities/", }); @@ -727,9 +728,9 @@ export const getSensitivities = ( * Get metadata for all 3D grid models, including bbox, dimensions and properties */ export const getGridModelsInfo = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/grid3d/grid_models_info/", }); @@ -740,9 +741,9 @@ export const getGridModelsInfo = ( * Get a grid */ export const getGridSurface = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/grid3d/grid_surface", }); @@ -753,9 +754,9 @@ export const getGridSurface = ( * Get a grid parameter */ export const getGridParameter = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/grid3d/grid_parameter", }); @@ -765,11 +766,11 @@ export const getGridParameter = ( * Post Get Polyline Intersection */ export const postGetPolylineIntersection = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).post< - PostGetPolylineIntersectionResponse, - PostGetPolylineIntersectionError, + PostGetPolylineIntersectionResponse_api, + PostGetPolylineIntersectionError_api, ThrowOnError >({ ...options, @@ -785,11 +786,11 @@ export const postGetPolylineIntersection = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetRealizationFlowNetworkResponse, - GetRealizationFlowNetworkError, + GetRealizationFlowNetworkResponse_api, + GetRealizationFlowNetworkError_api, ThrowOnError >({ ...options, @@ -802,9 +803,9 @@ export const getRealizationFlowNetwork = ( * Get pvt table data for a given Sumo ensemble and realization */ export const getTableData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/pvt/table_data/", }); @@ -814,9 +815,9 @@ export const getTableData = ( * Get Well Completions Data */ export const getWellCompletionsData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/well_completions/well_completions_data/", }); @@ -827,11 +828,11 @@ export const getWellCompletionsData = ( * Get wellbore headers for all wells in the field */ export const getDrilledWellboreHeaders = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetDrilledWellboreHeadersResponse, - GetDrilledWellboreHeadersError, + GetDrilledWellboreHeadersResponse_api, + GetDrilledWellboreHeadersError_api, ThrowOnError >({ ...options, @@ -844,9 +845,9 @@ export const getDrilledWellboreHeaders = ( * Get well trajectories for field */ export const getWellTrajectories = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/well/well_trajectories/", }); @@ -857,11 +858,11 @@ export const getWellTrajectories = ( * Get wellbore pick identifiers for field and stratigraphic column */ export const getWellborePickIdentifiers = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellborePickIdentifiersResponse, - GetWellborePickIdentifiersError, + GetWellborePickIdentifiersResponse_api, + GetWellborePickIdentifiersError_api, ThrowOnError >({ ...options, @@ -874,11 +875,11 @@ export const getWellborePickIdentifiers = * Get wellbore picks for field and pick identifier */ export const getWellborePicksForPickIdentifier = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellborePicksForPickIdentifierResponse, - GetWellborePicksForPickIdentifierError, + GetWellborePicksForPickIdentifierResponse_api, + GetWellborePicksForPickIdentifierError_api, ThrowOnError >({ ...options, @@ -891,11 +892,11 @@ export const getWellborePicksForPickIdentifier = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellborePicksForWellboreResponse, - GetWellborePicksForWellboreError, + GetWellborePicksForWellboreResponse_api, + GetWellborePicksForWellboreError_api, ThrowOnError >({ ...options, @@ -907,11 +908,11 @@ export const getWellborePicksForWellbore = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellborePicksInStratColumnResponse, - GetWellborePicksInStratColumnError, + GetWellborePicksInStratColumnResponse_api, + GetWellborePicksInStratColumnError_api, ThrowOnError >({ ...options, @@ -924,9 +925,9 @@ export const getWellborePicksInStratColumn = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/well/wellbore_completions/", }); @@ -937,9 +938,9 @@ export const getWellboreCompletions = ( * Get well bore casings for a single well bore */ export const getWellboreCasings = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/well/wellbore_casings/", }); @@ -950,9 +951,9 @@ export const getWellboreCasings = ( * Get well bore casing for a single well bore */ export const getWellborePerforations = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get( + return (options?.client ?? client).get( { ...options, url: "/well/wellbore_perforations/", @@ -966,11 +967,11 @@ export const getWellborePerforations = ( * Logs are available from multiple sources, which can be specificed by the "sources" parameter. */ export const getWellboreLogCurveHeaders = ( - options: Options, + options: Options, ) => { return (options?.client ?? client).get< - GetWellboreLogCurveHeadersResponse, - GetWellboreLogCurveHeadersError, + GetWellboreLogCurveHeadersResponse_api, + GetWellboreLogCurveHeadersError_api, ThrowOnError >({ ...options, @@ -983,9 +984,9 @@ export const getWellboreLogCurveHeaders = * Get log curve data */ export const getLogCurveData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/well/log_curve_data/", }); @@ -996,9 +997,9 @@ export const getLogCurveData = ( * Get a list of seismic cube meta. */ export const getSeismicCubeMetaList = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/seismic/seismic_cube_meta_list/", }); @@ -1009,9 +1010,9 @@ export const getSeismicCubeMetaList = ( * Get a seismic inline from a seismic cube. */ export const getInlineSlice = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/seismic/get_inline_slice/", }); @@ -1022,9 +1023,9 @@ export const getInlineSlice = ( * Get a seismic crossline from a seismic cube. */ export const getCrosslineSlice = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/seismic/get_crossline_slice/", }); @@ -1035,9 +1036,9 @@ export const getCrosslineSlice = ( * Get a seismic depth slice from a seismic cube. */ export const getDepthSlice = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/seismic/get_depth_slice/", }); @@ -1054,9 +1055,9 @@ export const getDepthSlice = ( * A SeismicFenceData object with fence traces in encoded 1D array, metadata for trace array decoding and fence min/max depth. */ export const postGetSeismicFence = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).post({ + return (options?.client ?? client).post({ ...options, headers: { "Content-Type": "application/json", @@ -1071,9 +1072,9 @@ export const postGetSeismicFence = ( * Get a directory of polygons in a Sumo ensemble */ export const getPolygonsDirectory = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/polygons/polygons_directory/", }); @@ -1083,9 +1084,9 @@ export const getPolygonsDirectory = ( * Get Polygons Data */ export const getPolygonsData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/polygons/polygons_data/", }); @@ -1096,9 +1097,9 @@ export const getPolygonsData = ( * Get username, display name and avatar from Microsoft Graph API for a given user id */ export const getUserPhoto = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/graph/user_photo/", }); @@ -1109,9 +1110,9 @@ export const getUserPhoto = ( * Retrieve all observations found in sumo case */ export const getObservations = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/observations/observations/", }); @@ -1121,9 +1122,9 @@ export const getObservations = ( * Get Table Definition */ export const getTableDefinition = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/rft/table_definition", }); @@ -1133,9 +1134,9 @@ export const getTableDefinition = ( * Get Realization Data */ export const getRealizationData = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/rft/realization_data", }); @@ -1145,9 +1146,9 @@ export const getRealizationData = ( * Get Vfp Table Names */ export const getVfpTableNames = ( - options: Options, + options: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/vfp/vfp_table_names/", }); @@ -1156,8 +1157,8 @@ export const getVfpTableNames = ( /** * Get Vfp Table */ -export const getVfpTable = (options: Options) => { - return (options?.client ?? client).get({ +export const getVfpTable = (options: Options) => { + return (options?.client ?? client).get({ ...options, url: "/vfp/vfp_table/", }); @@ -1166,8 +1167,8 @@ export const getVfpTable = (options: Optio /** * Login Route */ -export const loginRoute = (options?: Options) => { - return (options?.client ?? client).get({ +export const loginRoute = (options?: Options) => { + return (options?.client ?? client).get({ ...options, url: "/login", }); @@ -1177,7 +1178,7 @@ export const loginRoute = (options?: Optio * Authorized Callback Route */ export const authorizedCallbackRoute = ( - options?: Options, + options?: Options, ) => { return (options?.client ?? client).get({ ...options, @@ -1188,8 +1189,8 @@ export const authorizedCallbackRoute = ( /** * Get Alive */ -export const getAlive = (options?: Options) => { - return (options?.client ?? client).get({ +export const getAlive = (options?: Options) => { + return (options?.client ?? client).get({ ...options, url: "/alive", }); @@ -1199,9 +1200,9 @@ export const getAlive = (options?: Options * Get Alive Protected */ export const getAliveProtected = ( - options?: Options, + options?: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/alive_protected", }); @@ -1210,8 +1211,8 @@ export const getAliveProtected = ( /** * Post Logout */ -export const postLogout = (options?: Options) => { - return (options?.client ?? client).post({ +export const postLogout = (options?: Options) => { + return (options?.client ?? client).post({ ...options, url: "/logout", }); @@ -1221,9 +1222,9 @@ export const postLogout = (options?: Optio * Get Logged In User */ export const getLoggedInUser = ( - options?: Options, + options?: Options, ) => { - return (options?.client ?? client).get({ + return (options?.client ?? client).get({ ...options, url: "/logged_in_user", }); @@ -1232,8 +1233,8 @@ export const getLoggedInUser = ( /** * Root */ -export const root = (options?: Options) => { - return (options?.client ?? client).get({ +export const root = (options?: Options) => { + return (options?.client ?? client).get({ ...options, url: "/", }); diff --git a/frontend/src/api/autogen/types.gen.ts b/frontend/src/api/autogen/types.gen.ts index 76b11b6be..49a900037 100644 --- a/frontend/src/api/autogen/types.gen.ts +++ b/frontend/src/api/autogen/types.gen.ts @@ -1,6 +1,6 @@ // This file is auto-generated by @hey-api/openapi-ts -export enum Alq { +export enum Alq_api { GRAT = "GRAT", IGLR = "IGLR", TGLR = "TGLR", @@ -12,47 +12,47 @@ export enum Alq { "''" = "''", } -export type B64FloatArray = { +export type B64FloatArray_api = { element_type: "float32" | "float64"; data_b64str: string; }; -export type B64UintArray = { +export type B64UintArray_api = { element_type: "uint8" | "uint16" | "uint32" | "uint64"; data_b64str: string; }; -export type BodyPostGetAggregatedPerRealizationTableData = { +export type BodyPostGetAggregatedPerRealizationTableData_api = { /** * Selected identifiers and wanted values */ - identifiers_with_values: Array; + identifiers_with_values: Array; }; -export type BodyPostGetAggregatedStatisticalTableData = { +export type BodyPostGetAggregatedStatisticalTableData_api = { /** * Selected identifiers and wanted values */ - identifiers_with_values: Array; + identifiers_with_values: Array; }; -export type BodyPostGetPolylineIntersection = { +export type BodyPostGetPolylineIntersection_api = { polyline_utm_xy: Array; }; -export type BodyPostGetSampleSurfaceInPoints = { - sample_points: PointSetXy; +export type BodyPostGetSampleSurfaceInPoints_api = { + sample_points: PointSetXy_api; }; -export type BodyPostGetSeismicFence = { - polyline: SeismicFencePolyline; +export type BodyPostGetSeismicFence_api = { + polyline: SeismicFencePolyline_api; }; -export type BodyPostGetSurfaceIntersection = { - cumulative_length_polyline: SurfaceIntersectionCumulativeLengthPolyline; +export type BodyPostGetSurfaceIntersection_api = { + cumulative_length_polyline: SurfaceIntersectionCumulativeLengthPolyline_api; }; -export type BoundingBox2D = { +export type BoundingBox2D_api = { min_x: number; min_y: number; max_x: number; @@ -62,7 +62,7 @@ export type BoundingBox2D = { /** * Bounding box for a 3D grid geometry */ -export type BoundingBox3D = { +export type BoundingBox3D_api = { xmin: number; ymin: number; zmin: number; @@ -71,14 +71,14 @@ export type BoundingBox3D = { zmax: number; }; -export type CaseInfo = { +export type CaseInfo_api = { uuid: string; name: string; status: string; user: string; }; -export type Completions = { +export type Completions_api = { sortedCompletionDateIndices: Array; open: Array; shut: Array; @@ -87,17 +87,17 @@ export type Completions = { khMax: Array; }; -export type DatedFlowNetwork = { +export type DatedFlowNetwork_api = { dates: Array; - network: NetworkNode; + network: NetworkNode_api; }; -export type DerivedVectorInfo = { - type: DerivedVectorType; +export type DerivedVectorInfo_api = { + type: DerivedVectorType_api; sourceVector: string; }; -export enum DerivedVectorType { +export enum DerivedVectorType_api { PER_DAY = "PER_DAY", PER_INTVL = "PER_INTVL", } @@ -105,13 +105,13 @@ export enum DerivedVectorType { /** * Holds information that describes how a discrete curve value should be presented to the user. */ -export type DiscreteValueMetadata = { +export type DiscreteValueMetadata_api = { code: number; identifier: string; rgbColor: [number, number, number]; }; -export type EnsembleDetails = { +export type EnsembleDetails_api = { name: string; field_identifier: string; case_name: string; @@ -120,7 +120,7 @@ export type EnsembleDetails = { stratigraphic_column_identifier: string; }; -export type EnsembleInfo = { +export type EnsembleInfo_api = { name: string; realization_count: number; }; @@ -128,7 +128,7 @@ export type EnsembleInfo = { /** * Description/data for a single parameter in an ensemble */ -export type EnsembleParameter = { +export type EnsembleParameter_api = { name: string; is_logarithmic: boolean; is_discrete: boolean; @@ -139,7 +139,7 @@ export type EnsembleParameter = { values: Array | Array | Array; }; -export type EnsembleParameterDescription = { +export type EnsembleParameterDescription_api = { name: string; group_name: string | null; descriptive_name: string | null; @@ -149,7 +149,7 @@ export type EnsembleParameterDescription = { /** * A generic type for a scalar response from each of the members of the ensemble. */ -export type EnsembleScalarResponse = { +export type EnsembleScalarResponse_api = { realizations: Array; values: Array; name: string | null; @@ -159,49 +159,49 @@ export type EnsembleScalarResponse = { /** * Description/data for a single sensitivity in an ensemble */ -export type EnsembleSensitivity = { +export type EnsembleSensitivity_api = { name: string; - type: SensitivityType; - cases: Array; + type: SensitivityType_api; + cases: Array; }; /** * Description/data for a single sensitivity case in an ensemble */ -export type EnsembleSensitivityCase = { +export type EnsembleSensitivityCase_api = { name: string; realizations: Array; }; -export type FenceMeshSection = { - vertices_uz_b64arr: B64FloatArray; - poly_indices_b64arr: B64UintArray; - vertices_per_poly_b64arr: B64UintArray; - poly_source_cell_indices_b64arr: B64UintArray; - poly_props_b64arr: B64FloatArray; +export type FenceMeshSection_api = { + vertices_uz_b64arr: B64FloatArray_api; + poly_indices_b64arr: B64UintArray_api; + vertices_per_poly_b64arr: B64UintArray_api; + poly_source_cell_indices_b64arr: B64UintArray_api; + poly_props_b64arr: B64FloatArray_api; start_utm_x: number; start_utm_y: number; end_utm_x: number; end_utm_y: number; }; -export type FieldInfo = { +export type FieldInfo_api = { field_identifier: string; }; -export type FlowNetworkData = { - edgeMetadataList: Array; - nodeMetadataList: Array; - datedNetworks: Array; +export type FlowNetworkData_api = { + edgeMetadataList: Array; + nodeMetadataList: Array; + datedNetworks: Array; }; -export type FlowNetworkMetadata = { +export type FlowNetworkMetadata_api = { key: string; label: string; unit: string | null; }; -export enum FlowRateType { +export enum FlowRateType_api { OIL = "OIL", LIQ = "LIQ", GAS = "GAS", @@ -210,13 +210,13 @@ export enum FlowRateType { WAT = "WAT", } -export enum FluidZone { +export enum FluidZone_api { OIL = "Oil", GAS = "Gas", WATER = "Water", } -export enum Frequency { +export enum Frequency_api { DAILY = "DAILY", WEEKLY = "WEEKLY", MONTHLY = "MONTHLY", @@ -224,31 +224,31 @@ export enum Frequency { YEARLY = "YEARLY", } -export enum Gfr { +export enum Gfr_api { GOR = "GOR", GLR = "GLR", OGR = "OGR", MMW = "MMW", } -export type GraphUserPhoto = { +export type GraphUserPhoto_api = { avatar_b64str: string | null; }; /** * Specification of a 3D grid dimensions */ -export type Grid3dDimensions = { +export type Grid3dDimensions_api = { i_count: number; j_count: number; k_count: number; - subgrids: Array; + subgrids: Array; }; -export type Grid3dGeometry = { - polys_b64arr: B64UintArray; - points_b64arr: B64FloatArray; - poly_source_cell_indices_b64arr: B64UintArray; +export type Grid3dGeometry_api = { + polys_b64arr: B64UintArray_api; + points_b64arr: B64FloatArray_api; + poly_source_cell_indices_b64arr: B64UintArray_api; origin_utm_x: number; origin_utm_y: number; xmin: number; @@ -262,15 +262,15 @@ export type Grid3dGeometry = { /** * Metadata for a 3D grid model, including its properties and geometry */ -export type Grid3dInfo = { +export type Grid3dInfo_api = { grid_name: string; - bbox: BoundingBox3D; - dimensions: Grid3dDimensions; - property_info_arr: Array; + bbox: BoundingBox3D_api; + dimensions: Grid3dDimensions_api; + property_info_arr: Array; }; -export type Grid3dMappedProperty = { - poly_props_b64arr: B64FloatArray; +export type Grid3dMappedProperty_api = { + poly_props_b64arr: B64FloatArray_api; min_grid_prop_value: number; max_grid_prop_value: number; }; @@ -278,7 +278,7 @@ export type Grid3dMappedProperty = { /** * Metadata for a 3D grid property */ -export type Grid3dPropertyInfo = { +export type Grid3dPropertyInfo_api = { property_name: string; iso_date_or_interval: string | null; }; @@ -286,20 +286,20 @@ export type Grid3dPropertyInfo = { /** * Named subset of 3D grid layers (Zone) */ -export type Grid3dZone = { +export type Grid3dZone_api = { name: string; start_layer: number; end_layer: number; }; -export type GridDimensions = { +export type GridDimensions_api = { i_count: number; j_count: number; k_count: number; }; -export type HttpValidationError = { - detail?: Array; +export type HttpValidationError_api = { + detail?: Array; }; /** @@ -307,10 +307,10 @@ export type HttpValidationError = { * * Contains data for a single fluid zone, e.g. Oil, Gas, Water, or sum of fluid zones */ -export type InplaceStatisticalVolumetricTableData = { +export type InplaceStatisticalVolumetricTableData_api = { fluidSelectionName: string; - selectorColumns: Array; - resultColumnStatistics: Array; + selectorColumns: Array; + resultColumnStatistics: Array; }; /** @@ -318,14 +318,14 @@ export type InplaceStatisticalVolumetricTableData = { * * Fluid selection can be single fluid zones, e.g. Oil, Gas, Water, or sum of fluid zones - Oil + Gas + Water */ -export type InplaceStatisticalVolumetricTableDataPerFluidSelection = { - tableDataPerFluidSelection: Array; +export type InplaceStatisticalVolumetricTableDataPerFluidSelection_api = { + tableDataPerFluidSelection: Array; }; /** * Allowed volumetric response names */ -export enum InplaceVolumetricResultName { +export enum InplaceVolumetricResultName_api { BULK = "BULK", NET = "NET", PORO = "PORO", @@ -347,7 +347,7 @@ export enum InplaceVolumetricResultName { /** * Definition of possible statistics for a result column in an inplace volumetrics table */ -export enum InplaceVolumetricStatistic { +export enum InplaceVolumetricStatistic_api { MEAN = "mean", STDDEV = "stddev", MAX = "max", @@ -361,10 +361,10 @@ export enum InplaceVolumetricStatistic { * * Contains data for a single fluid zone, e.g. Oil, Gas, Water, or sum of fluid zones */ -export type InplaceVolumetricTableData = { +export type InplaceVolumetricTableData_api = { fluidSelectionName: string; - selectorColumns: Array; - resultColumns: Array; + selectorColumns: Array; + resultColumns: Array; }; /** @@ -372,11 +372,11 @@ export type InplaceVolumetricTableData = { * * Fluid selection can be single fluid zones, e.g. Oil, Gas, Water, or sum of fluid zones - Oil + Gas + Water */ -export type InplaceVolumetricTableDataPerFluidSelection = { - tableDataPerFluidSelection: Array; +export type InplaceVolumetricTableDataPerFluidSelection_api = { + tableDataPerFluidSelection: Array; }; -export enum InplaceVolumetricsIdentifier { +export enum InplaceVolumetricsIdentifier_api { ZONE = "ZONE", REGION = "REGION", FACIES = "FACIES", @@ -387,22 +387,22 @@ export enum InplaceVolumetricsIdentifier { * Unique values for an index column in a volumetric table * All values should ideally be strings, but it is common to see integers, especially for REGION */ -export type InplaceVolumetricsIdentifierWithValues = { - identifier: InplaceVolumetricsIdentifier; +export type InplaceVolumetricsIdentifierWithValues_api = { + identifier: InplaceVolumetricsIdentifier_api; values: Array; }; /** * Definition of a volumetric table */ -export type InplaceVolumetricsTableDefinition = { +export type InplaceVolumetricsTableDefinition_api = { tableName: string; - fluidZones: Array; - resultNames: Array; - identifiersWithValues: Array; + fluidZones: Array; + resultNames: Array; + identifiersWithValues: Array; }; -export type NetworkNode = { +export type NetworkNode_api = { node_type: "Group" | "Well"; node_label: string; edge_label: string; @@ -412,10 +412,10 @@ export type NetworkNode = { edge_data: { [key: string]: Array; }; - children: Array; + children: Array; }; -export enum NodeType { +export enum NodeType_api { PROD = "prod", INJ = "inj", OTHER = "other", @@ -424,17 +424,17 @@ export enum NodeType { /** * A collection of observations associated with a field/case/ensemble */ -export type Observations = { - summary: Array; - rft: Array; +export type Observations_api = { + summary: Array; + rft: Array; }; -export type PointSetXy = { +export type PointSetXy_api = { x_points: Array; y_points: Array; }; -export type PolygonData = { +export type PolygonData_api = { x_arr: Array; y_arr: Array; z_arr: Array; @@ -444,7 +444,7 @@ export type PolygonData = { /** * To be revisited later when the metadata is more mature. */ -export enum PolygonsAttributeType { +export enum PolygonsAttributeType_api { DEPTH = "depth", TIME = "time", PROPERTY = "property", @@ -459,24 +459,24 @@ export enum PolygonsAttributeType { NAMED_AREA = "named_area", } -export type PolygonsMeta = { +export type PolygonsMeta_api = { name: string; name_is_stratigraphic_offical: boolean; stratigraphic_identifier: string | null; relative_stratigraphic_level: number | null; parent_stratigraphic_identifier: string | null; attribute_name: string; - attribute_type: PolygonsAttributeType; + attribute_type: PolygonsAttributeType_api; }; -export type PolylineIntersection = { - fence_mesh_sections: Array; - grid_dimensions: GridDimensions; +export type PolylineIntersection_api = { + fence_mesh_sections: Array; + grid_dimensions: GridDimensions_api; min_grid_prop_value: number; max_grid_prop_value: number; }; -export type PvtData = { +export type PvtData_api = { name: string; phase: string; pvtnum: number; @@ -500,7 +500,7 @@ export type PvtData = { * - unique_values: List of unique values in the column * - indices: List of indices, in unique_values list, for each row in the table */ -export type RepeatedTableColumnData = { +export type RepeatedTableColumnData_api = { columnName: string; uniqueValues: Array; indices: Array; @@ -519,7 +519,7 @@ export type RepeatedTableColumnData = { * y (float): Y utm coordinate of the observation. * z (float): Z utm coordinate of the observation. */ -export type RftObservation = { +export type RftObservation_api = { value: number; comment: string | null; error: number; @@ -537,16 +537,16 @@ export type RftObservation = { * well (str): Unique well identifier * date (str): Observation date * comment (Optional[str]): An optional comment associated with the collection of observations. - * observations (List[RftObservation]): A list of RFT observations associated with this collection. + * observations (List[RftObservation_api]): A list of RFT observations associated with this collection. */ -export type RftObservations = { +export type RftObservations_api = { well: string; date: string; comment: string | null; - observations: Array; + observations: Array; }; -export type RftRealizationData = { +export type RftRealizationData_api = { well_name: string; realization: number; timestamp_utc_ms: number; @@ -554,12 +554,12 @@ export type RftRealizationData = { value_arr: Array; }; -export type RftTableDefinition = { +export type RftTableDefinition_api = { response_names: Array; - well_infos: Array; + well_infos: Array; }; -export type RftWellInfo = { +export type RftWellInfo_api = { well_name: string; timestamps_utc_ms: Array; }; @@ -567,14 +567,14 @@ export type RftWellInfo = { /** * Metadata for a seismic cube. */ -export type SeismicCubeMeta = { +export type SeismicCubeMeta_api = { seismicAttribute: string; unit: string; isoDateOrInterval: string; isObservation: boolean; isDepth: boolean; - bbox: BoundingBox3D; - spec: SeismicCubeSpec; + bbox: BoundingBox3D_api; + spec: SeismicCubeSpec_api; }; /** @@ -594,7 +594,7 @@ export type SeismicCubeMeta = { * - `zFlip`: {-1, 1} - The flip factor for the z-direction (1 if not flipped, -1 if flipped). * - `rotationDeg`: The rotation angle of the cube [deg]. */ -export type SeismicCubeSpec = { +export type SeismicCubeSpec_api = { numCols: number; numRows: number; numLayers: number; @@ -633,8 +633,8 @@ export type SeismicCubeSpec = { * See: * - VdsAxis: https://github.com/equinor/vds-slice/blob/ab6f39789bf3d3b59a8df14f1c4682d340dc0bf3/internal/core/core.go#L37-L55 */ -export type SeismicFenceData = { - fence_traces_b64arr: B64FloatArray; +export type SeismicFenceData_api = { + fence_traces_b64arr: B64FloatArray_api; num_traces: number; num_samples_per_trace: number; min_fence_depth: number; @@ -653,7 +653,7 @@ export type SeismicFenceData = { * - Consider points_xy: List[float] - i.e. array with [x1, y1, x2, y2, ..., xn, yn] instead of x_points and y_points arrays? * - Ensure equal length of x_points and y_points arrays? */ -export type SeismicFencePolyline = { +export type SeismicFencePolyline_api = { x_points: Array; y_points: Array; }; @@ -661,7 +661,7 @@ export type SeismicFencePolyline = { /** * Definition of a seismic slice from a seismic cube. This could be an inline, crossline, or depth slice. * u and v axes are the respective domain coordinate system axes, and the slice traces are the seismic data values. - * The SeismicCubeMeta specification object (not part of this schema) provides a transformation matrix for converting + * The SeismicCubeMeta_api specification object (not part of this schema) provides a transformation matrix for converting * the slice data from its own coordinate system (u,v) to the global coordinate system. * * `Properties:` @@ -680,8 +680,8 @@ export type SeismicFencePolyline = { * * Fence traces 1D array: [trace_1_sample_1, trace_1_sample_2, ..., trace_1_sample_n, ..., trace_m_sample_1, trace_m_sample_2, ..., trace_m_sample_n] */ -export type SeismicSliceData = { - slice_traces_b64arr: B64FloatArray; +export type SeismicSliceData_api = { + slice_traces_b64arr: B64FloatArray_api; bbox_utm: Array>; u_min: number; u_max: number; @@ -695,12 +695,12 @@ export type SeismicSliceData = { value_max: number; }; -export enum SensitivityType { +export enum SensitivityType_api { MONTECARLO = "montecarlo", SCENARIO = "scenario", } -export enum StatisticFunction { +export enum StatisticFunction_api { MEAN = "MEAN", MIN = "MIN", MAX = "MAX", @@ -709,15 +709,15 @@ export enum StatisticFunction { P50 = "P50", } -export type StatisticValueObject = { - statisticFunction: StatisticFunction; +export type StatisticValueObject_api = { + statisticFunction: StatisticFunction_api; values: Array; }; /** * Stratigraphic column from SMDA */ -export type StratigraphicColumn = { +export type StratigraphicColumn_api = { identifier: string; areaType: string; status: string; @@ -729,7 +729,7 @@ export type StratigraphicColumn = { * * Camel case attributes needed for esvIntersection component in front-end */ -export type StratigraphicUnit = { +export type StratigraphicUnit_api = { identifier: string; top: string; base: string; @@ -747,7 +747,7 @@ export type StratigraphicUnit = { /** * A single observation of a summary vector at a specific date. */ -export type SummaryVectorDateObservation = { +export type SummaryVectorDateObservation_api = { date: string; comment: string | null; value: number; @@ -758,10 +758,10 @@ export type SummaryVectorDateObservation = { /** * A collection of observations of a summary vector. */ -export type SummaryVectorObservations = { +export type SummaryVectorObservations_api = { vector_name: string; comment: string | null; - observations: Array; + observations: Array; }; /** @@ -774,7 +774,7 @@ export type SummaryVectorObservations = { * * To be revisited later when the metadata is more mature. */ -export enum SurfaceAttributeType { +export enum SurfaceAttributeType_api { DEPTH = "depth", FACIES_THICKNESS = "facies_thickness", FLUID_CONTACT = "fluid_contact", @@ -789,25 +789,25 @@ export enum SurfaceAttributeType { UNKNOWN = "UNKNOWN", } -export type SurfaceDataFloat = { +export type SurfaceDataFloat_api = { format: "float"; - surface_def: SurfaceDef; - transformed_bbox_utm: BoundingBox2D; + surface_def: SurfaceDef_api; + transformed_bbox_utm: BoundingBox2D_api; value_min: number; value_max: number; - values_b64arr: B64FloatArray; + values_b64arr: B64FloatArray_api; }; -export type SurfaceDataPng = { +export type SurfaceDataPng_api = { format: "png"; - surface_def: SurfaceDef; - transformed_bbox_utm: BoundingBox2D; + surface_def: SurfaceDef_api; + transformed_bbox_utm: BoundingBox2D_api; value_min: number; value_max: number; png_image_base64: string; }; -export type SurfaceDef = { +export type SurfaceDef_api = { npoints_x: number; npoints_y: number; inc_x: number; @@ -833,7 +833,7 @@ export type SurfaceDef = { * * Note: Verify if cum_lengths is necessary with respect to xtgeo */ -export type SurfaceIntersectionCumulativeLengthPolyline = { +export type SurfaceIntersectionCumulativeLengthPolyline_api = { x_points: Array; y_points: Array; cum_lengths: Array; @@ -846,36 +846,36 @@ export type SurfaceIntersectionCumulativeLengthPolyline = { * z_points: Array of z-points (depth values) at the intersection points, i.e. depth value for each (x,y) point. * cum_lengths: Cumulative length values at the intersection points, i.e. accumulated length between each element in the z points. */ -export type SurfaceIntersectionData = { +export type SurfaceIntersectionData_api = { name: string; z_points: Array; cum_lengths: Array; }; -export type SurfaceMeta = { +export type SurfaceMeta_api = { name: string; name_is_stratigraphic_offical: boolean; attribute_name: string; - attribute_type: SurfaceAttributeType; - time_type: SurfaceTimeType; + attribute_type: SurfaceAttributeType_api; + time_type: SurfaceTimeType_api; is_observation: boolean; value_min: number | null; value_max: number | null; }; -export type SurfaceMetaSet = { - surfaces: Array; +export type SurfaceMetaSet_api = { + surfaces: Array; time_points_iso_str: Array; time_intervals_iso_str: Array; surface_names_in_strat_order: Array; }; -export type SurfaceRealizationSampleValues = { +export type SurfaceRealizationSampleValues_api = { realization: number; sampled_values: Array; }; -export enum SurfaceStatisticFunction { +export enum SurfaceStatisticFunction_api { MEAN = "MEAN", STD = "STD", MIN = "MIN", @@ -885,15 +885,15 @@ export enum SurfaceStatisticFunction { P50 = "P50", } -export enum SurfaceTimeType { +export enum SurfaceTimeType_api { NO_TIME = "NO_TIME", TIME_POINT = "TIME_POINT", INTERVAL = "INTERVAL", } -export type Thp = "THP"; +export type Thp_api = "THP"; -export enum TabType { +export enum TabType_api { BHP = "BHP", TEMP = "TEMP", } @@ -903,7 +903,7 @@ export enum TabType { * * Length of column values should be equal to the number of rows in the table */ -export type TableColumnData = { +export type TableColumnData_api = { columnName: string; columnValues: Array; }; @@ -913,14 +913,14 @@ export type TableColumnData = { * * Length of column values should be equal to the number of rows in the table */ -export type TableColumnStatisticalData = { +export type TableColumnStatisticalData_api = { columnName: string; statisticValues: { [key: string]: Array; }; }; -export enum UnitType { +export enum UnitType_api { METRIC = "METRIC", FIELD = "FIELD", LAB = "LAB", @@ -928,7 +928,7 @@ export enum UnitType { DEFAULT = "DEFAULT", } -export type UserInfo = { +export type UserInfo_api = { username: string; display_name: string | null; avatar_b64str: string | null; @@ -936,61 +936,61 @@ export type UserInfo = { has_smda_access: boolean; }; -export type ValidationError = { +export type ValidationError_api = { loc: Array; msg: string; type: string; }; -export type VectorDescription = { +export type VectorDescription_api = { name: string; descriptiveName: string; hasHistorical: boolean; - derivedVectorInfo: DerivedVectorInfo | null; + derivedVectorInfo: DerivedVectorInfo_api | null; }; -export type VectorHistoricalData = { +export type VectorHistoricalData_api = { timestampsUtcMs: Array; values: Array; unit: string; isRate: boolean; }; -export type VectorRealizationData = { +export type VectorRealizationData_api = { realization: number; timestampsUtcMs: Array; values: Array; unit: string; isRate: boolean; - derivedVectorInfo: DerivedVectorInfo | null; + derivedVectorInfo: DerivedVectorInfo_api | null; }; -export type VectorStatisticData = { +export type VectorStatisticData_api = { realizations: Array; timestampsUtcMs: Array; - valueObjects: Array; + valueObjects: Array; unit: string; isRate: boolean; - derivedVectorInfo: DerivedVectorInfo | null; + derivedVectorInfo: DerivedVectorInfo_api | null; }; -export type VectorStatisticSensitivityData = { +export type VectorStatisticSensitivityData_api = { realizations: Array; timestampsUtcMs: Array; - valueObjects: Array; + valueObjects: Array; unit: string; isRate: boolean; sensitivityName: string; sensitivityCase: string; }; -export type VfpInjTable = { +export type VfpInjTable_api = { vfpType: "INJ"; tableNumber: number; datum: number; - flowRateType: FlowRateType; - unitType: UnitType; - tabType: TabType; + flowRateType: FlowRateType_api; + unitType: UnitType_api; + tabType: TabType_api; thpValues: Array; flowRateValues: Array; bhpValues: Array; @@ -999,23 +999,23 @@ export type VfpInjTable = { bhpUnit: string; }; -export type VfpProdTable = { +export type VfpProdTable_api = { vfpType: "PROD"; tableNumber: number; datum: number; - flowRateType: FlowRateType; - unitType: UnitType; - tabType: TabType; + flowRateType: FlowRateType_api; + unitType: UnitType_api; + tabType: TabType_api; thpValues: Array; flowRateValues: Array; bhpValues: Array; flowRateUnit: string; thpUnit: string; bhpUnit: string; - thpType: Thp; - wfrType: Wfr; - gfrType: Gfr; - alqType: Alq; + thpType: Thp_api; + wfrType: Wfr_api; + gfrType: Gfr_api; + alqType: Alq_api; wfrValues: Array; gfrValues: Array; alqValues: Array; @@ -1024,7 +1024,7 @@ export type VfpProdTable = { alqUnit: string; }; -export enum Wfr { +export enum Wfr_api { WOR = "WOR", WCT = "WCT", WGR = "WGR", @@ -1035,51 +1035,51 @@ export enum Wfr { /** * Type definition for well completions data */ -export type WellCompletionsData = { +export type WellCompletionsData_api = { version: string; - units: WellCompletionsUnits; - zones: Array; + units: WellCompletionsUnits_api; + zones: Array; sortedCompletionDates: Array; - wells: Array; + wells: Array; }; -export type WellCompletionsUnitInfo = { +export type WellCompletionsUnitInfo_api = { unit: string; decimalPlaces: number; }; -export type WellCompletionsUnits = { - kh: WellCompletionsUnitInfo; +export type WellCompletionsUnits_api = { + kh: WellCompletionsUnitInfo_api; }; -export type WellCompletionsWell = { +export type WellCompletionsWell_api = { name: string; attributes: { [key: string]: string | number | boolean; }; completions: { - [key: string]: Completions; + [key: string]: Completions_api; }; }; -export type WellCompletionsZone = { +export type WellCompletionsZone_api = { name: string; - subzones: Array | null; + subzones: Array | null; }; -export enum WellLogCurveSourceEnum { +export enum WellLogCurveSourceEnum_api { SSDL_WELL_LOG = "ssdl.well_log", SMDA_GEOLOGY = "smda.geology", SMDA_STRATIGRAPHY = "smda.stratigraphy", } -export enum WellLogCurveTypeEnum { +export enum WellLogCurveTypeEnum_api { CONTINUOUS = "continuous", DISCRETE = "discrete", FLAG = "flag", } -export type WellboreCasing = { +export type WellboreCasing_api = { itemType: string; diameterNumeric: number; diameterInner: number; @@ -1092,7 +1092,7 @@ export type WellboreCasing = { endDepth: number; }; -export type WellboreCompletion = { +export type WellboreCompletion_api = { mdTop: number; mdBottom: number; tvdTop: number | null; @@ -1102,7 +1102,7 @@ export type WellboreCompletion = { comment: string | null; }; -export type WellboreHeader = { +export type WellboreHeader_api = { wellboreUuid: string; uniqueWellboreIdentifier: string; wellUuid: string; @@ -1115,8 +1115,8 @@ export type WellboreHeader = { wellboreStatus: string; }; -export type WellboreLogCurveData = { - source: WellLogCurveSourceEnum; +export type WellboreLogCurveData_api = { + source: WellLogCurveSourceEnum_api; name: string; logName: string; indexMin: number; @@ -1130,18 +1130,18 @@ export type WellboreLogCurveData = { unit: string | null; curveUnitDesc: string | null; dataPoints: Array<[number, number | string | null]>; - discreteValueMetadata: Array | null; + discreteValueMetadata: Array | null; }; -export type WellboreLogCurveHeader = { - source: WellLogCurveSourceEnum; - curveType: WellLogCurveTypeEnum; +export type WellboreLogCurveHeader_api = { + source: WellLogCurveSourceEnum_api; + curveType: WellLogCurveTypeEnum_api; logName: string; curveName: string; curveUnit: string | null; }; -export type WellborePerforation = { +export type WellborePerforation_api = { mdTop: number; mdBottom: number; tvdTop: number; @@ -1155,7 +1155,7 @@ export type WellborePerforation = { * * Camel case attributes needed for esvIntersection component in front-end */ -export type WellborePick = { +export type WellborePick_api = { northing: number; easting: number; tvd: number; @@ -1171,7 +1171,7 @@ export type WellborePick = { interpreter: string | null; }; -export type WellboreTrajectory = { +export type WellboreTrajectory_api = { wellboreUuid: string; uniqueWellboreIdentifier: string; tvdMslArr: Array; @@ -1180,23 +1180,23 @@ export type WellboreTrajectory = { northingArr: Array; }; -export type GetFieldsData = { +export type GetFieldsData_api = { body?: never; path?: never; query?: never; url: "/fields"; }; -export type GetFieldsResponses = { +export type GetFieldsResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetFieldsResponse = GetFieldsResponses[keyof GetFieldsResponses]; +export type GetFieldsResponse_api = GetFieldsResponses_api[keyof GetFieldsResponses_api]; -export type GetCasesData = { +export type GetCasesData_api = { body?: never; path?: never; query: { @@ -1208,25 +1208,25 @@ export type GetCasesData = { url: "/cases"; }; -export type GetCasesErrors = { +export type GetCasesErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetCasesError = GetCasesErrors[keyof GetCasesErrors]; +export type GetCasesError_api = GetCasesErrors_api[keyof GetCasesErrors_api]; -export type GetCasesResponses = { +export type GetCasesResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetCasesResponse = GetCasesResponses[keyof GetCasesResponses]; +export type GetCasesResponse_api = GetCasesResponses_api[keyof GetCasesResponses_api]; -export type GetEnsemblesData = { +export type GetEnsemblesData_api = { body?: never; path: { /** @@ -1238,25 +1238,25 @@ export type GetEnsemblesData = { url: "/cases/{case_uuid}/ensembles"; }; -export type GetEnsemblesErrors = { +export type GetEnsemblesErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetEnsemblesError = GetEnsemblesErrors[keyof GetEnsemblesErrors]; +export type GetEnsemblesError_api = GetEnsemblesErrors_api[keyof GetEnsemblesErrors_api]; -export type GetEnsemblesResponses = { +export type GetEnsemblesResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetEnsemblesResponse = GetEnsemblesResponses[keyof GetEnsemblesResponses]; +export type GetEnsemblesResponse_api = GetEnsemblesResponses_api[keyof GetEnsemblesResponses_api]; -export type GetEnsembleDetailsData = { +export type GetEnsembleDetailsData_api = { body?: never; path: { /** @@ -1272,25 +1272,25 @@ export type GetEnsembleDetailsData = { url: "/cases/{case_uuid}/ensembles/{ensemble_name}"; }; -export type GetEnsembleDetailsErrors = { +export type GetEnsembleDetailsErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetEnsembleDetailsError = GetEnsembleDetailsErrors[keyof GetEnsembleDetailsErrors]; +export type GetEnsembleDetailsError_api = GetEnsembleDetailsErrors_api[keyof GetEnsembleDetailsErrors_api]; -export type GetEnsembleDetailsResponses = { +export type GetEnsembleDetailsResponses_api = { /** * Successful Response */ - 200: EnsembleDetails; + 200: EnsembleDetails_api; }; -export type GetEnsembleDetailsResponse = GetEnsembleDetailsResponses[keyof GetEnsembleDetailsResponses]; +export type GetEnsembleDetailsResponse_api = GetEnsembleDetailsResponses_api[keyof GetEnsembleDetailsResponses_api]; -export type GetVectorListData = { +export type GetVectorListData_api = { body?: never; path?: never; query: { @@ -1310,25 +1310,25 @@ export type GetVectorListData = { url: "/timeseries/vector_list/"; }; -export type GetVectorListErrors = { +export type GetVectorListErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetVectorListError = GetVectorListErrors[keyof GetVectorListErrors]; +export type GetVectorListError_api = GetVectorListErrors_api[keyof GetVectorListErrors_api]; -export type GetVectorListResponses = { +export type GetVectorListResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetVectorListResponse = GetVectorListResponses[keyof GetVectorListResponses]; +export type GetVectorListResponse_api = GetVectorListResponses_api[keyof GetVectorListResponses_api]; -export type GetDeltaEnsembleVectorListData = { +export type GetDeltaEnsembleVectorListData_api = { body?: never; path?: never; query: { @@ -1356,26 +1356,26 @@ export type GetDeltaEnsembleVectorListData = { url: "/timeseries/delta_ensemble_vector_list/"; }; -export type GetDeltaEnsembleVectorListErrors = { +export type GetDeltaEnsembleVectorListErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetDeltaEnsembleVectorListError = GetDeltaEnsembleVectorListErrors[keyof GetDeltaEnsembleVectorListErrors]; +export type GetDeltaEnsembleVectorListError_api = GetDeltaEnsembleVectorListErrors_api[keyof GetDeltaEnsembleVectorListErrors_api]; -export type GetDeltaEnsembleVectorListResponses = { +export type GetDeltaEnsembleVectorListResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetDeltaEnsembleVectorListResponse = - GetDeltaEnsembleVectorListResponses[keyof GetDeltaEnsembleVectorListResponses]; +export type GetDeltaEnsembleVectorListResponse_api = + GetDeltaEnsembleVectorListResponses_api[keyof GetDeltaEnsembleVectorListResponses_api]; -export type GetRealizationsVectorDataData = { +export type GetRealizationsVectorDataData_api = { body?: never; path?: never; query: { @@ -1394,7 +1394,7 @@ export type GetRealizationsVectorDataData = { /** * Resampling frequency. If not specified, raw data without resampling wil be returned. */ - resampling_frequency?: Frequency | null; + resampling_frequency?: Frequency_api | null; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1403,26 +1403,26 @@ export type GetRealizationsVectorDataData = { url: "/timeseries/realizations_vector_data/"; }; -export type GetRealizationsVectorDataErrors = { +export type GetRealizationsVectorDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetRealizationsVectorDataError = GetRealizationsVectorDataErrors[keyof GetRealizationsVectorDataErrors]; +export type GetRealizationsVectorDataError_api = GetRealizationsVectorDataErrors_api[keyof GetRealizationsVectorDataErrors_api]; -export type GetRealizationsVectorDataResponses = { +export type GetRealizationsVectorDataResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetRealizationsVectorDataResponse = - GetRealizationsVectorDataResponses[keyof GetRealizationsVectorDataResponses]; +export type GetRealizationsVectorDataResponse_api = + GetRealizationsVectorDataResponses_api[keyof GetRealizationsVectorDataResponses_api]; -export type GetDeltaEnsembleRealizationsVectorDataData = { +export type GetDeltaEnsembleRealizationsVectorDataData_api = { body?: never; path?: never; query: { @@ -1449,7 +1449,7 @@ export type GetDeltaEnsembleRealizationsVectorDataData = { /** * Resampling frequency */ - resampling_frequency: Frequency; + resampling_frequency: Frequency_api; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1458,27 +1458,27 @@ export type GetDeltaEnsembleRealizationsVectorDataData = { url: "/timeseries/delta_ensemble_realizations_vector_data/"; }; -export type GetDeltaEnsembleRealizationsVectorDataErrors = { +export type GetDeltaEnsembleRealizationsVectorDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetDeltaEnsembleRealizationsVectorDataError = - GetDeltaEnsembleRealizationsVectorDataErrors[keyof GetDeltaEnsembleRealizationsVectorDataErrors]; +export type GetDeltaEnsembleRealizationsVectorDataError_api = + GetDeltaEnsembleRealizationsVectorDataErrors_api[keyof GetDeltaEnsembleRealizationsVectorDataErrors_api]; -export type GetDeltaEnsembleRealizationsVectorDataResponses = { +export type GetDeltaEnsembleRealizationsVectorDataResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetDeltaEnsembleRealizationsVectorDataResponse = - GetDeltaEnsembleRealizationsVectorDataResponses[keyof GetDeltaEnsembleRealizationsVectorDataResponses]; +export type GetDeltaEnsembleRealizationsVectorDataResponse_api = + GetDeltaEnsembleRealizationsVectorDataResponses_api[keyof GetDeltaEnsembleRealizationsVectorDataResponses_api]; -export type GetTimestampsListData = { +export type GetTimestampsListData_api = { body?: never; path?: never; query: { @@ -1493,30 +1493,30 @@ export type GetTimestampsListData = { /** * Resampling frequency */ - resampling_frequency?: Frequency | null; + resampling_frequency?: Frequency_api | null; }; url: "/timeseries/timestamps_list/"; }; -export type GetTimestampsListErrors = { +export type GetTimestampsListErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetTimestampsListError = GetTimestampsListErrors[keyof GetTimestampsListErrors]; +export type GetTimestampsListError_api = GetTimestampsListErrors_api[keyof GetTimestampsListErrors_api]; -export type GetTimestampsListResponses = { +export type GetTimestampsListResponses_api = { /** * Successful Response */ 200: Array; }; -export type GetTimestampsListResponse = GetTimestampsListResponses[keyof GetTimestampsListResponses]; +export type GetTimestampsListResponse_api = GetTimestampsListResponses_api[keyof GetTimestampsListResponses_api]; -export type GetHistoricalVectorDataData = { +export type GetHistoricalVectorDataData_api = { body?: never; path?: never; query: { @@ -1535,30 +1535,30 @@ export type GetHistoricalVectorDataData = { /** * Resampling frequency */ - resampling_frequency?: Frequency | null; + resampling_frequency?: Frequency_api | null; }; url: "/timeseries/historical_vector_data/"; }; -export type GetHistoricalVectorDataErrors = { +export type GetHistoricalVectorDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetHistoricalVectorDataError = GetHistoricalVectorDataErrors[keyof GetHistoricalVectorDataErrors]; +export type GetHistoricalVectorDataError_api = GetHistoricalVectorDataErrors_api[keyof GetHistoricalVectorDataErrors_api]; -export type GetHistoricalVectorDataResponses = { +export type GetHistoricalVectorDataResponses_api = { /** * Successful Response */ - 200: VectorHistoricalData; + 200: VectorHistoricalData_api; }; -export type GetHistoricalVectorDataResponse = GetHistoricalVectorDataResponses[keyof GetHistoricalVectorDataResponses]; +export type GetHistoricalVectorDataResponse_api = GetHistoricalVectorDataResponses_api[keyof GetHistoricalVectorDataResponses_api]; -export type GetStatisticalVectorDataData = { +export type GetStatisticalVectorDataData_api = { body?: never; path?: never; query: { @@ -1577,11 +1577,11 @@ export type GetStatisticalVectorDataData = { /** * Resampling frequency */ - resampling_frequency: Frequency; + resampling_frequency: Frequency_api; /** * Optional list of statistics to calculate. If not specified, all statistics will be calculated. */ - statistic_functions?: Array | null; + statistic_functions?: Array | null; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1590,26 +1590,26 @@ export type GetStatisticalVectorDataData = { url: "/timeseries/statistical_vector_data/"; }; -export type GetStatisticalVectorDataErrors = { +export type GetStatisticalVectorDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetStatisticalVectorDataError = GetStatisticalVectorDataErrors[keyof GetStatisticalVectorDataErrors]; +export type GetStatisticalVectorDataError_api = GetStatisticalVectorDataErrors_api[keyof GetStatisticalVectorDataErrors_api]; -export type GetStatisticalVectorDataResponses = { +export type GetStatisticalVectorDataResponses_api = { /** * Successful Response */ - 200: VectorStatisticData; + 200: VectorStatisticData_api; }; -export type GetStatisticalVectorDataResponse = - GetStatisticalVectorDataResponses[keyof GetStatisticalVectorDataResponses]; +export type GetStatisticalVectorDataResponse_api = + GetStatisticalVectorDataResponses_api[keyof GetStatisticalVectorDataResponses_api]; -export type GetDeltaEnsembleStatisticalVectorDataData = { +export type GetDeltaEnsembleStatisticalVectorDataData_api = { body?: never; path?: never; query: { @@ -1636,11 +1636,11 @@ export type GetDeltaEnsembleStatisticalVectorDataData = { /** * Resampling frequency */ - resampling_frequency: Frequency; + resampling_frequency: Frequency_api; /** * Optional list of statistics to calculate. If not specified, all statistics will be calculated. */ - statistic_functions?: Array | null; + statistic_functions?: Array | null; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1649,27 +1649,27 @@ export type GetDeltaEnsembleStatisticalVectorDataData = { url: "/timeseries/delta_ensemble_statistical_vector_data/"; }; -export type GetDeltaEnsembleStatisticalVectorDataErrors = { +export type GetDeltaEnsembleStatisticalVectorDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetDeltaEnsembleStatisticalVectorDataError = - GetDeltaEnsembleStatisticalVectorDataErrors[keyof GetDeltaEnsembleStatisticalVectorDataErrors]; +export type GetDeltaEnsembleStatisticalVectorDataError_api = + GetDeltaEnsembleStatisticalVectorDataErrors_api[keyof GetDeltaEnsembleStatisticalVectorDataErrors_api]; -export type GetDeltaEnsembleStatisticalVectorDataResponses = { +export type GetDeltaEnsembleStatisticalVectorDataResponses_api = { /** * Successful Response */ - 200: VectorStatisticData; + 200: VectorStatisticData_api; }; -export type GetDeltaEnsembleStatisticalVectorDataResponse = - GetDeltaEnsembleStatisticalVectorDataResponses[keyof GetDeltaEnsembleStatisticalVectorDataResponses]; +export type GetDeltaEnsembleStatisticalVectorDataResponse_api = + GetDeltaEnsembleStatisticalVectorDataResponses_api[keyof GetDeltaEnsembleStatisticalVectorDataResponses_api]; -export type GetStatisticalVectorDataPerSensitivityData = { +export type GetStatisticalVectorDataPerSensitivityData_api = { body?: never; path?: never; query: { @@ -1688,11 +1688,11 @@ export type GetStatisticalVectorDataPerSensitivityData = { /** * Resampling frequency */ - resampling_frequency: Frequency; + resampling_frequency: Frequency_api; /** * Optional list of statistics to calculate. If not specified, all statistics will be calculated. */ - statistic_functions?: Array | null; + statistic_functions?: Array | null; /** * Optional list of realizations to include. If not specified, all realizations will be included. */ @@ -1701,27 +1701,27 @@ export type GetStatisticalVectorDataPerSensitivityData = { url: "/timeseries/statistical_vector_data_per_sensitivity/"; }; -export type GetStatisticalVectorDataPerSensitivityErrors = { +export type GetStatisticalVectorDataPerSensitivityErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetStatisticalVectorDataPerSensitivityError = - GetStatisticalVectorDataPerSensitivityErrors[keyof GetStatisticalVectorDataPerSensitivityErrors]; +export type GetStatisticalVectorDataPerSensitivityError_api = + GetStatisticalVectorDataPerSensitivityErrors_api[keyof GetStatisticalVectorDataPerSensitivityErrors_api]; -export type GetStatisticalVectorDataPerSensitivityResponses = { +export type GetStatisticalVectorDataPerSensitivityResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetStatisticalVectorDataPerSensitivityResponse = - GetStatisticalVectorDataPerSensitivityResponses[keyof GetStatisticalVectorDataPerSensitivityResponses]; +export type GetStatisticalVectorDataPerSensitivityResponse_api = + GetStatisticalVectorDataPerSensitivityResponses_api[keyof GetStatisticalVectorDataPerSensitivityResponses_api]; -export type GetRealizationVectorAtTimestampData = { +export type GetRealizationVectorAtTimestampData_api = { body?: never; path?: never; query: { @@ -1745,27 +1745,27 @@ export type GetRealizationVectorAtTimestampData = { url: "/timeseries/realization_vector_at_timestamp/"; }; -export type GetRealizationVectorAtTimestampErrors = { +export type GetRealizationVectorAtTimestampErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetRealizationVectorAtTimestampError = - GetRealizationVectorAtTimestampErrors[keyof GetRealizationVectorAtTimestampErrors]; +export type GetRealizationVectorAtTimestampError_api = + GetRealizationVectorAtTimestampErrors_api[keyof GetRealizationVectorAtTimestampErrors_api]; -export type GetRealizationVectorAtTimestampResponses = { +export type GetRealizationVectorAtTimestampResponses_api = { /** * Successful Response */ - 200: EnsembleScalarResponse; + 200: EnsembleScalarResponse_api; }; -export type GetRealizationVectorAtTimestampResponse = - GetRealizationVectorAtTimestampResponses[keyof GetRealizationVectorAtTimestampResponses]; +export type GetRealizationVectorAtTimestampResponse_api = + GetRealizationVectorAtTimestampResponses_api[keyof GetRealizationVectorAtTimestampResponses_api]; -export type GetTableDefinitionsData = { +export type GetTableDefinitionsData_api = { body?: never; path?: never; query: { @@ -1781,26 +1781,26 @@ export type GetTableDefinitionsData = { url: "/inplace_volumetrics/table_definitions/"; }; -export type GetTableDefinitionsErrors = { +export type GetTableDefinitionsErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetTableDefinitionsError = GetTableDefinitionsErrors[keyof GetTableDefinitionsErrors]; +export type GetTableDefinitionsError_api = GetTableDefinitionsErrors_api[keyof GetTableDefinitionsErrors_api]; -export type GetTableDefinitionsResponses = { +export type GetTableDefinitionsResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetTableDefinitionsResponse = GetTableDefinitionsResponses[keyof GetTableDefinitionsResponses]; +export type GetTableDefinitionsResponse_api = GetTableDefinitionsResponses_api[keyof GetTableDefinitionsResponses_api]; -export type PostGetAggregatedPerRealizationTableDataData = { - body: BodyPostGetAggregatedPerRealizationTableData; +export type PostGetAggregatedPerRealizationTableDataData_api = { + body: BodyPostGetAggregatedPerRealizationTableData_api; path?: never; query: { /** @@ -1822,7 +1822,7 @@ export type PostGetAggregatedPerRealizationTableDataData = { /** * The fluid zones to aggregate by */ - fluid_zones: Array; + fluid_zones: Array; /** * Whether to accumulate fluid zones */ @@ -1830,7 +1830,7 @@ export type PostGetAggregatedPerRealizationTableDataData = { /** * The identifiers to group table data by */ - group_by_identifiers?: Array | null; + group_by_identifiers?: Array | null; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1839,28 +1839,28 @@ export type PostGetAggregatedPerRealizationTableDataData = { url: "/inplace_volumetrics/get_aggregated_per_realization_table_data/"; }; -export type PostGetAggregatedPerRealizationTableDataErrors = { +export type PostGetAggregatedPerRealizationTableDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type PostGetAggregatedPerRealizationTableDataError = - PostGetAggregatedPerRealizationTableDataErrors[keyof PostGetAggregatedPerRealizationTableDataErrors]; +export type PostGetAggregatedPerRealizationTableDataError_api = + PostGetAggregatedPerRealizationTableDataErrors_api[keyof PostGetAggregatedPerRealizationTableDataErrors_api]; -export type PostGetAggregatedPerRealizationTableDataResponses = { +export type PostGetAggregatedPerRealizationTableDataResponses_api = { /** * Successful Response */ - 200: InplaceVolumetricTableDataPerFluidSelection; + 200: InplaceVolumetricTableDataPerFluidSelection_api; }; -export type PostGetAggregatedPerRealizationTableDataResponse = - PostGetAggregatedPerRealizationTableDataResponses[keyof PostGetAggregatedPerRealizationTableDataResponses]; +export type PostGetAggregatedPerRealizationTableDataResponse_api = + PostGetAggregatedPerRealizationTableDataResponses_api[keyof PostGetAggregatedPerRealizationTableDataResponses_api]; -export type PostGetAggregatedStatisticalTableDataData = { - body: BodyPostGetAggregatedStatisticalTableData; +export type PostGetAggregatedStatisticalTableDataData_api = { + body: BodyPostGetAggregatedStatisticalTableData_api; path?: never; query: { /** @@ -1882,7 +1882,7 @@ export type PostGetAggregatedStatisticalTableDataData = { /** * The fluid zones to aggregate by */ - fluid_zones: Array; + fluid_zones: Array; /** * Whether to accumulate fluid zones */ @@ -1890,7 +1890,7 @@ export type PostGetAggregatedStatisticalTableDataData = { /** * The identifiers to group table data by */ - group_by_identifiers?: Array | null; + group_by_identifiers?: Array | null; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -1899,27 +1899,27 @@ export type PostGetAggregatedStatisticalTableDataData = { url: "/inplace_volumetrics/get_aggregated_statistical_table_data/"; }; -export type PostGetAggregatedStatisticalTableDataErrors = { +export type PostGetAggregatedStatisticalTableDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type PostGetAggregatedStatisticalTableDataError = - PostGetAggregatedStatisticalTableDataErrors[keyof PostGetAggregatedStatisticalTableDataErrors]; +export type PostGetAggregatedStatisticalTableDataError_api = + PostGetAggregatedStatisticalTableDataErrors_api[keyof PostGetAggregatedStatisticalTableDataErrors_api]; -export type PostGetAggregatedStatisticalTableDataResponses = { +export type PostGetAggregatedStatisticalTableDataResponses_api = { /** * Successful Response */ - 200: InplaceStatisticalVolumetricTableDataPerFluidSelection; + 200: InplaceStatisticalVolumetricTableDataPerFluidSelection_api; }; -export type PostGetAggregatedStatisticalTableDataResponse = - PostGetAggregatedStatisticalTableDataResponses[keyof PostGetAggregatedStatisticalTableDataResponses]; +export type PostGetAggregatedStatisticalTableDataResponse_api = + PostGetAggregatedStatisticalTableDataResponses_api[keyof PostGetAggregatedStatisticalTableDataResponses_api]; -export type GetRealizationSurfacesMetadataData = { +export type GetRealizationSurfacesMetadataData_api = { body?: never; path?: never; query: { @@ -1935,27 +1935,27 @@ export type GetRealizationSurfacesMetadataData = { url: "/surface/realization_surfaces_metadata/"; }; -export type GetRealizationSurfacesMetadataErrors = { +export type GetRealizationSurfacesMetadataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetRealizationSurfacesMetadataError = - GetRealizationSurfacesMetadataErrors[keyof GetRealizationSurfacesMetadataErrors]; +export type GetRealizationSurfacesMetadataError_api = + GetRealizationSurfacesMetadataErrors_api[keyof GetRealizationSurfacesMetadataErrors_api]; -export type GetRealizationSurfacesMetadataResponses = { +export type GetRealizationSurfacesMetadataResponses_api = { /** * Successful Response */ - 200: SurfaceMetaSet; + 200: SurfaceMetaSet_api; }; -export type GetRealizationSurfacesMetadataResponse = - GetRealizationSurfacesMetadataResponses[keyof GetRealizationSurfacesMetadataResponses]; +export type GetRealizationSurfacesMetadataResponse_api = + GetRealizationSurfacesMetadataResponses_api[keyof GetRealizationSurfacesMetadataResponses_api]; -export type GetObservedSurfacesMetadataData = { +export type GetObservedSurfacesMetadataData_api = { body?: never; path?: never; query: { @@ -1967,27 +1967,27 @@ export type GetObservedSurfacesMetadataData = { url: "/surface/observed_surfaces_metadata/"; }; -export type GetObservedSurfacesMetadataErrors = { +export type GetObservedSurfacesMetadataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetObservedSurfacesMetadataError = - GetObservedSurfacesMetadataErrors[keyof GetObservedSurfacesMetadataErrors]; +export type GetObservedSurfacesMetadataError_api = + GetObservedSurfacesMetadataErrors_api[keyof GetObservedSurfacesMetadataErrors_api]; -export type GetObservedSurfacesMetadataResponses = { +export type GetObservedSurfacesMetadataResponses_api = { /** * Successful Response */ - 200: SurfaceMetaSet; + 200: SurfaceMetaSet_api; }; -export type GetObservedSurfacesMetadataResponse = - GetObservedSurfacesMetadataResponses[keyof GetObservedSurfacesMetadataResponses]; +export type GetObservedSurfacesMetadataResponse_api = + GetObservedSurfacesMetadataResponses_api[keyof GetObservedSurfacesMetadataResponses_api]; -export type GetSurfaceDataData = { +export type GetSurfaceDataData_api = { body?: never; path?: never; query: { @@ -2000,33 +2000,33 @@ export type GetSurfaceDataData = { */ data_format?: "float" | "png"; /** - * Definition of the surface onto which the data should be resampled. *SurfaceDef* object properties encoded as a `KeyValStr` string. + * Definition of the surface onto which the data should be resampled. *SurfaceDef_api* object properties encoded as a `KeyValStr` string. */ resample_to_def_str?: string | null; }; url: "/surface/surface_data"; }; -export type GetSurfaceDataErrors = { +export type GetSurfaceDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetSurfaceDataError = GetSurfaceDataErrors[keyof GetSurfaceDataErrors]; +export type GetSurfaceDataError_api = GetSurfaceDataErrors_api[keyof GetSurfaceDataErrors_api]; -export type GetSurfaceDataResponses = { +export type GetSurfaceDataResponses_api = { /** * Successful Response */ - 200: SurfaceDataFloat | SurfaceDataPng; + 200: SurfaceDataFloat_api | SurfaceDataPng_api; }; -export type GetSurfaceDataResponse = GetSurfaceDataResponses[keyof GetSurfaceDataResponses]; +export type GetSurfaceDataResponse_api = GetSurfaceDataResponses_api[keyof GetSurfaceDataResponses_api]; -export type PostGetSurfaceIntersectionData = { - body: BodyPostGetSurfaceIntersection; +export type PostGetSurfaceIntersectionData_api = { + body: BodyPostGetSurfaceIntersection_api; path?: never; query: { /** @@ -2057,27 +2057,27 @@ export type PostGetSurfaceIntersectionData = { url: "/surface/get_surface_intersection"; }; -export type PostGetSurfaceIntersectionErrors = { +export type PostGetSurfaceIntersectionErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type PostGetSurfaceIntersectionError = PostGetSurfaceIntersectionErrors[keyof PostGetSurfaceIntersectionErrors]; +export type PostGetSurfaceIntersectionError_api = PostGetSurfaceIntersectionErrors_api[keyof PostGetSurfaceIntersectionErrors_api]; -export type PostGetSurfaceIntersectionResponses = { +export type PostGetSurfaceIntersectionResponses_api = { /** * Successful Response */ - 200: SurfaceIntersectionData; + 200: SurfaceIntersectionData_api; }; -export type PostGetSurfaceIntersectionResponse = - PostGetSurfaceIntersectionResponses[keyof PostGetSurfaceIntersectionResponses]; +export type PostGetSurfaceIntersectionResponse_api = + PostGetSurfaceIntersectionResponses_api[keyof PostGetSurfaceIntersectionResponses_api]; -export type PostGetSampleSurfaceInPointsData = { - body: BodyPostGetSampleSurfaceInPoints; +export type PostGetSampleSurfaceInPointsData_api = { + body: BodyPostGetSampleSurfaceInPoints_api; path?: never; query: { /** @@ -2104,27 +2104,27 @@ export type PostGetSampleSurfaceInPointsData = { url: "/surface/get_sample_surface_in_points"; }; -export type PostGetSampleSurfaceInPointsErrors = { +export type PostGetSampleSurfaceInPointsErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type PostGetSampleSurfaceInPointsError = - PostGetSampleSurfaceInPointsErrors[keyof PostGetSampleSurfaceInPointsErrors]; +export type PostGetSampleSurfaceInPointsError_api = + PostGetSampleSurfaceInPointsErrors_api[keyof PostGetSampleSurfaceInPointsErrors_api]; -export type PostGetSampleSurfaceInPointsResponses = { +export type PostGetSampleSurfaceInPointsResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type PostGetSampleSurfaceInPointsResponse = - PostGetSampleSurfaceInPointsResponses[keyof PostGetSampleSurfaceInPointsResponses]; +export type PostGetSampleSurfaceInPointsResponse_api = + PostGetSampleSurfaceInPointsResponses_api[keyof PostGetSampleSurfaceInPointsResponses_api]; -export type GetDeltaSurfaceDataData = { +export type GetDeltaSurfaceDataData_api = { body?: never; path?: never; query: { @@ -2141,32 +2141,32 @@ export type GetDeltaSurfaceDataData = { */ data_format?: "float" | "png"; /** - * Definition of the surface onto which the data should be resampled. *SurfaceDef* object properties encoded as a `KeyValStr` string. + * Definition of the surface onto which the data should be resampled. *SurfaceDef_api* object properties encoded as a `KeyValStr` string. */ resample_to_def_str?: string | null; }; url: "/surface/delta_surface_data"; }; -export type GetDeltaSurfaceDataErrors = { +export type GetDeltaSurfaceDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetDeltaSurfaceDataError = GetDeltaSurfaceDataErrors[keyof GetDeltaSurfaceDataErrors]; +export type GetDeltaSurfaceDataError_api = GetDeltaSurfaceDataErrors_api[keyof GetDeltaSurfaceDataErrors_api]; -export type GetDeltaSurfaceDataResponses = { +export type GetDeltaSurfaceDataResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetDeltaSurfaceDataResponse = GetDeltaSurfaceDataResponses[keyof GetDeltaSurfaceDataResponses]; +export type GetDeltaSurfaceDataResponse_api = GetDeltaSurfaceDataResponses_api[keyof GetDeltaSurfaceDataResponses_api]; -export type GetMisfitSurfaceDataData = { +export type GetMisfitSurfaceDataData_api = { body?: never; path?: never; query: { @@ -2181,7 +2181,7 @@ export type GetMisfitSurfaceDataData = { /** * Statistics to calculate */ - statistic_functions: Array; + statistic_functions: Array; /** * Optional list of realizations encoded as string to include. If not specified, all realizations will be included. */ @@ -2191,32 +2191,32 @@ export type GetMisfitSurfaceDataData = { */ data_format?: "float" | "png"; /** - * Definition of the surface onto which the data should be resampled. *SurfaceDef* object properties encoded as a `KeyValStr` string. + * Definition of the surface onto which the data should be resampled. *SurfaceDef_api* object properties encoded as a `KeyValStr` string. */ resample_to_def_str?: string | null; }; url: "/surface/misfit_surface_data"; }; -export type GetMisfitSurfaceDataErrors = { +export type GetMisfitSurfaceDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetMisfitSurfaceDataError = GetMisfitSurfaceDataErrors[keyof GetMisfitSurfaceDataErrors]; +export type GetMisfitSurfaceDataError_api = GetMisfitSurfaceDataErrors_api[keyof GetMisfitSurfaceDataErrors_api]; -export type GetMisfitSurfaceDataResponses = { +export type GetMisfitSurfaceDataResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetMisfitSurfaceDataResponse = GetMisfitSurfaceDataResponses[keyof GetMisfitSurfaceDataResponses]; +export type GetMisfitSurfaceDataResponse_api = GetMisfitSurfaceDataResponses_api[keyof GetMisfitSurfaceDataResponses_api]; -export type GetWellboreStratigraphicColumnsData = { +export type GetWellboreStratigraphicColumnsData_api = { body?: never; path?: never; query: { @@ -2228,27 +2228,27 @@ export type GetWellboreStratigraphicColumnsData = { url: "/surface/wellbore_stratigraphic_columns/"; }; -export type GetWellboreStratigraphicColumnsErrors = { +export type GetWellboreStratigraphicColumnsErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetWellboreStratigraphicColumnsError = - GetWellboreStratigraphicColumnsErrors[keyof GetWellboreStratigraphicColumnsErrors]; +export type GetWellboreStratigraphicColumnsError_api = + GetWellboreStratigraphicColumnsErrors_api[keyof GetWellboreStratigraphicColumnsErrors_api]; -export type GetWellboreStratigraphicColumnsResponses = { +export type GetWellboreStratigraphicColumnsResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellboreStratigraphicColumnsResponse = - GetWellboreStratigraphicColumnsResponses[keyof GetWellboreStratigraphicColumnsResponses]; +export type GetWellboreStratigraphicColumnsResponse_api = + GetWellboreStratigraphicColumnsResponses_api[keyof GetWellboreStratigraphicColumnsResponses_api]; -export type GetStratigraphicUnitsData = { +export type GetStratigraphicUnitsData_api = { body?: never; path?: never; query: { @@ -2260,25 +2260,25 @@ export type GetStratigraphicUnitsData = { url: "/surface/stratigraphic_units"; }; -export type GetStratigraphicUnitsErrors = { +export type GetStratigraphicUnitsErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetStratigraphicUnitsError = GetStratigraphicUnitsErrors[keyof GetStratigraphicUnitsErrors]; +export type GetStratigraphicUnitsError_api = GetStratigraphicUnitsErrors_api[keyof GetStratigraphicUnitsErrors_api]; -export type GetStratigraphicUnitsResponses = { +export type GetStratigraphicUnitsResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetStratigraphicUnitsResponse = GetStratigraphicUnitsResponses[keyof GetStratigraphicUnitsResponses]; +export type GetStratigraphicUnitsResponse_api = GetStratigraphicUnitsResponses_api[keyof GetStratigraphicUnitsResponses_api]; -export type GetParameterNamesAndDescriptionData = { +export type GetParameterNamesAndDescriptionData_api = { body?: never; path?: never; query: { @@ -2302,27 +2302,27 @@ export type GetParameterNamesAndDescriptionData = { url: "/parameters/parameter_names_and_description/"; }; -export type GetParameterNamesAndDescriptionErrors = { +export type GetParameterNamesAndDescriptionErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetParameterNamesAndDescriptionError = - GetParameterNamesAndDescriptionErrors[keyof GetParameterNamesAndDescriptionErrors]; +export type GetParameterNamesAndDescriptionError_api = + GetParameterNamesAndDescriptionErrors_api[keyof GetParameterNamesAndDescriptionErrors_api]; -export type GetParameterNamesAndDescriptionResponses = { +export type GetParameterNamesAndDescriptionResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetParameterNamesAndDescriptionResponse = - GetParameterNamesAndDescriptionResponses[keyof GetParameterNamesAndDescriptionResponses]; +export type GetParameterNamesAndDescriptionResponse_api = + GetParameterNamesAndDescriptionResponses_api[keyof GetParameterNamesAndDescriptionResponses_api]; -export type GetParameterData = { +export type GetParameterData_api = { body?: never; path?: never; query: { @@ -2342,25 +2342,25 @@ export type GetParameterData = { url: "/parameters/parameter/"; }; -export type GetParameterErrors = { +export type GetParameterErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetParameterError = GetParameterErrors[keyof GetParameterErrors]; +export type GetParameterError_api = GetParameterErrors_api[keyof GetParameterErrors_api]; -export type GetParameterResponses = { +export type GetParameterResponses_api = { /** * Successful Response */ - 200: EnsembleParameter | null; + 200: EnsembleParameter_api | null; }; -export type GetParameterResponse = GetParameterResponses[keyof GetParameterResponses]; +export type GetParameterResponse_api = GetParameterResponses_api[keyof GetParameterResponses_api]; -export type GetParametersData = { +export type GetParametersData_api = { body?: never; path?: never; query: { @@ -2376,25 +2376,25 @@ export type GetParametersData = { url: "/parameters/parameters/"; }; -export type GetParametersErrors = { +export type GetParametersErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetParametersError = GetParametersErrors[keyof GetParametersErrors]; +export type GetParametersError_api = GetParametersErrors_api[keyof GetParametersErrors_api]; -export type GetParametersResponses = { +export type GetParametersResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetParametersResponse = GetParametersResponses[keyof GetParametersResponses]; +export type GetParametersResponse_api = GetParametersResponses_api[keyof GetParametersResponses_api]; -export type GetIsSensitivityRunData = { +export type GetIsSensitivityRunData_api = { body?: never; path?: never; query: { @@ -2410,25 +2410,25 @@ export type GetIsSensitivityRunData = { url: "/parameters/is_sensitivity_run/"; }; -export type GetIsSensitivityRunErrors = { +export type GetIsSensitivityRunErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetIsSensitivityRunError = GetIsSensitivityRunErrors[keyof GetIsSensitivityRunErrors]; +export type GetIsSensitivityRunError_api = GetIsSensitivityRunErrors_api[keyof GetIsSensitivityRunErrors_api]; -export type GetIsSensitivityRunResponses = { +export type GetIsSensitivityRunResponses_api = { /** * Successful Response */ 200: boolean; }; -export type GetIsSensitivityRunResponse = GetIsSensitivityRunResponses[keyof GetIsSensitivityRunResponses]; +export type GetIsSensitivityRunResponse_api = GetIsSensitivityRunResponses_api[keyof GetIsSensitivityRunResponses_api]; -export type GetSensitivitiesData = { +export type GetSensitivitiesData_api = { body?: never; path?: never; query: { @@ -2444,25 +2444,25 @@ export type GetSensitivitiesData = { url: "/parameters/sensitivities/"; }; -export type GetSensitivitiesErrors = { +export type GetSensitivitiesErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetSensitivitiesError = GetSensitivitiesErrors[keyof GetSensitivitiesErrors]; +export type GetSensitivitiesError_api = GetSensitivitiesErrors_api[keyof GetSensitivitiesErrors_api]; -export type GetSensitivitiesResponses = { +export type GetSensitivitiesResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetSensitivitiesResponse = GetSensitivitiesResponses[keyof GetSensitivitiesResponses]; +export type GetSensitivitiesResponse_api = GetSensitivitiesResponses_api[keyof GetSensitivitiesResponses_api]; -export type GetGridModelsInfoData = { +export type GetGridModelsInfoData_api = { body?: never; path?: never; query: { @@ -2482,25 +2482,25 @@ export type GetGridModelsInfoData = { url: "/grid3d/grid_models_info/"; }; -export type GetGridModelsInfoErrors = { +export type GetGridModelsInfoErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetGridModelsInfoError = GetGridModelsInfoErrors[keyof GetGridModelsInfoErrors]; +export type GetGridModelsInfoError_api = GetGridModelsInfoErrors_api[keyof GetGridModelsInfoErrors_api]; -export type GetGridModelsInfoResponses = { +export type GetGridModelsInfoResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetGridModelsInfoResponse = GetGridModelsInfoResponses[keyof GetGridModelsInfoResponses]; +export type GetGridModelsInfoResponse_api = GetGridModelsInfoResponses_api[keyof GetGridModelsInfoResponses_api]; -export type GetGridSurfaceData = { +export type GetGridSurfaceData_api = { body?: never; path?: never; query: { @@ -2548,25 +2548,25 @@ export type GetGridSurfaceData = { url: "/grid3d/grid_surface"; }; -export type GetGridSurfaceErrors = { +export type GetGridSurfaceErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetGridSurfaceError = GetGridSurfaceErrors[keyof GetGridSurfaceErrors]; +export type GetGridSurfaceError_api = GetGridSurfaceErrors_api[keyof GetGridSurfaceErrors_api]; -export type GetGridSurfaceResponses = { +export type GetGridSurfaceResponses_api = { /** * Successful Response */ - 200: Grid3dGeometry; + 200: Grid3dGeometry_api; }; -export type GetGridSurfaceResponse = GetGridSurfaceResponses[keyof GetGridSurfaceResponses]; +export type GetGridSurfaceResponse_api = GetGridSurfaceResponses_api[keyof GetGridSurfaceResponses_api]; -export type GetGridParameterData = { +export type GetGridParameterData_api = { body?: never; path?: never; query: { @@ -2622,26 +2622,26 @@ export type GetGridParameterData = { url: "/grid3d/grid_parameter"; }; -export type GetGridParameterErrors = { +export type GetGridParameterErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetGridParameterError = GetGridParameterErrors[keyof GetGridParameterErrors]; +export type GetGridParameterError_api = GetGridParameterErrors_api[keyof GetGridParameterErrors_api]; -export type GetGridParameterResponses = { +export type GetGridParameterResponses_api = { /** * Successful Response */ - 200: Grid3dMappedProperty; + 200: Grid3dMappedProperty_api; }; -export type GetGridParameterResponse = GetGridParameterResponses[keyof GetGridParameterResponses]; +export type GetGridParameterResponse_api = GetGridParameterResponses_api[keyof GetGridParameterResponses_api]; -export type PostGetPolylineIntersectionData = { - body: BodyPostGetPolylineIntersection; +export type PostGetPolylineIntersectionData_api = { + body: BodyPostGetPolylineIntersection_api; path?: never; query: { /** @@ -2672,27 +2672,27 @@ export type PostGetPolylineIntersectionData = { url: "/grid3d/get_polyline_intersection"; }; -export type PostGetPolylineIntersectionErrors = { +export type PostGetPolylineIntersectionErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type PostGetPolylineIntersectionError = - PostGetPolylineIntersectionErrors[keyof PostGetPolylineIntersectionErrors]; +export type PostGetPolylineIntersectionError_api = + PostGetPolylineIntersectionErrors_api[keyof PostGetPolylineIntersectionErrors_api]; -export type PostGetPolylineIntersectionResponses = { +export type PostGetPolylineIntersectionResponses_api = { /** * Successful Response */ - 200: PolylineIntersection; + 200: PolylineIntersection_api; }; -export type PostGetPolylineIntersectionResponse = - PostGetPolylineIntersectionResponses[keyof PostGetPolylineIntersectionResponses]; +export type PostGetPolylineIntersectionResponse_api = + PostGetPolylineIntersectionResponses_api[keyof PostGetPolylineIntersectionResponses_api]; -export type GetRealizationFlowNetworkData = { +export type GetRealizationFlowNetworkData_api = { body?: never; path?: never; query: { @@ -2711,35 +2711,35 @@ export type GetRealizationFlowNetworkData = { /** * Resampling frequency */ - resampling_frequency: Frequency; + resampling_frequency: Frequency_api; /** * Node types */ - node_type_set: Array; + node_type_set: Array; }; url: "/flow_network/realization_flow_network/"; }; -export type GetRealizationFlowNetworkErrors = { +export type GetRealizationFlowNetworkErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetRealizationFlowNetworkError = GetRealizationFlowNetworkErrors[keyof GetRealizationFlowNetworkErrors]; +export type GetRealizationFlowNetworkError_api = GetRealizationFlowNetworkErrors_api[keyof GetRealizationFlowNetworkErrors_api]; -export type GetRealizationFlowNetworkResponses = { +export type GetRealizationFlowNetworkResponses_api = { /** * Successful Response */ - 200: FlowNetworkData; + 200: FlowNetworkData_api; }; -export type GetRealizationFlowNetworkResponse = - GetRealizationFlowNetworkResponses[keyof GetRealizationFlowNetworkResponses]; +export type GetRealizationFlowNetworkResponse_api = + GetRealizationFlowNetworkResponses_api[keyof GetRealizationFlowNetworkResponses_api]; -export type GetTableDataData = { +export type GetTableDataData_api = { body?: never; path?: never; query: { @@ -2759,25 +2759,25 @@ export type GetTableDataData = { url: "/pvt/table_data/"; }; -export type GetTableDataErrors = { +export type GetTableDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetTableDataError = GetTableDataErrors[keyof GetTableDataErrors]; +export type GetTableDataError_api = GetTableDataErrors_api[keyof GetTableDataErrors_api]; -export type GetTableDataResponses = { +export type GetTableDataResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetTableDataResponse = GetTableDataResponses[keyof GetTableDataResponses]; +export type GetTableDataResponse_api = GetTableDataResponses_api[keyof GetTableDataResponses_api]; -export type GetWellCompletionsDataData = { +export type GetWellCompletionsDataData_api = { body?: never; path?: never; query: { @@ -2797,25 +2797,25 @@ export type GetWellCompletionsDataData = { url: "/well_completions/well_completions_data/"; }; -export type GetWellCompletionsDataErrors = { +export type GetWellCompletionsDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetWellCompletionsDataError = GetWellCompletionsDataErrors[keyof GetWellCompletionsDataErrors]; +export type GetWellCompletionsDataError_api = GetWellCompletionsDataErrors_api[keyof GetWellCompletionsDataErrors_api]; -export type GetWellCompletionsDataResponses = { +export type GetWellCompletionsDataResponses_api = { /** * Successful Response */ - 200: WellCompletionsData; + 200: WellCompletionsData_api; }; -export type GetWellCompletionsDataResponse = GetWellCompletionsDataResponses[keyof GetWellCompletionsDataResponses]; +export type GetWellCompletionsDataResponse_api = GetWellCompletionsDataResponses_api[keyof GetWellCompletionsDataResponses_api]; -export type GetDrilledWellboreHeadersData = { +export type GetDrilledWellboreHeadersData_api = { body?: never; path?: never; query: { @@ -2827,26 +2827,26 @@ export type GetDrilledWellboreHeadersData = { url: "/well/drilled_wellbore_headers/"; }; -export type GetDrilledWellboreHeadersErrors = { +export type GetDrilledWellboreHeadersErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetDrilledWellboreHeadersError = GetDrilledWellboreHeadersErrors[keyof GetDrilledWellboreHeadersErrors]; +export type GetDrilledWellboreHeadersError_api = GetDrilledWellboreHeadersErrors_api[keyof GetDrilledWellboreHeadersErrors_api]; -export type GetDrilledWellboreHeadersResponses = { +export type GetDrilledWellboreHeadersResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetDrilledWellboreHeadersResponse = - GetDrilledWellboreHeadersResponses[keyof GetDrilledWellboreHeadersResponses]; +export type GetDrilledWellboreHeadersResponse_api = + GetDrilledWellboreHeadersResponses_api[keyof GetDrilledWellboreHeadersResponses_api]; -export type GetWellTrajectoriesData = { +export type GetWellTrajectoriesData_api = { body?: never; path?: never; query: { @@ -2862,25 +2862,25 @@ export type GetWellTrajectoriesData = { url: "/well/well_trajectories/"; }; -export type GetWellTrajectoriesErrors = { +export type GetWellTrajectoriesErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetWellTrajectoriesError = GetWellTrajectoriesErrors[keyof GetWellTrajectoriesErrors]; +export type GetWellTrajectoriesError_api = GetWellTrajectoriesErrors_api[keyof GetWellTrajectoriesErrors_api]; -export type GetWellTrajectoriesResponses = { +export type GetWellTrajectoriesResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellTrajectoriesResponse = GetWellTrajectoriesResponses[keyof GetWellTrajectoriesResponses]; +export type GetWellTrajectoriesResponse_api = GetWellTrajectoriesResponses_api[keyof GetWellTrajectoriesResponses_api]; -export type GetWellborePickIdentifiersData = { +export type GetWellborePickIdentifiersData_api = { body?: never; path?: never; query: { @@ -2892,26 +2892,26 @@ export type GetWellborePickIdentifiersData = { url: "/well/wellbore_pick_identifiers/"; }; -export type GetWellborePickIdentifiersErrors = { +export type GetWellborePickIdentifiersErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetWellborePickIdentifiersError = GetWellborePickIdentifiersErrors[keyof GetWellborePickIdentifiersErrors]; +export type GetWellborePickIdentifiersError_api = GetWellborePickIdentifiersErrors_api[keyof GetWellborePickIdentifiersErrors_api]; -export type GetWellborePickIdentifiersResponses = { +export type GetWellborePickIdentifiersResponses_api = { /** * Successful Response */ 200: Array; }; -export type GetWellborePickIdentifiersResponse = - GetWellborePickIdentifiersResponses[keyof GetWellborePickIdentifiersResponses]; +export type GetWellborePickIdentifiersResponse_api = + GetWellborePickIdentifiersResponses_api[keyof GetWellborePickIdentifiersResponses_api]; -export type GetWellborePicksForPickIdentifierData = { +export type GetWellborePicksForPickIdentifierData_api = { body?: never; path?: never; query: { @@ -2927,27 +2927,27 @@ export type GetWellborePicksForPickIdentifierData = { url: "/well/wellbore_picks_for_pick_identifier/"; }; -export type GetWellborePicksForPickIdentifierErrors = { +export type GetWellborePicksForPickIdentifierErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetWellborePicksForPickIdentifierError = - GetWellborePicksForPickIdentifierErrors[keyof GetWellborePicksForPickIdentifierErrors]; +export type GetWellborePicksForPickIdentifierError_api = + GetWellborePicksForPickIdentifierErrors_api[keyof GetWellborePicksForPickIdentifierErrors_api]; -export type GetWellborePicksForPickIdentifierResponses = { +export type GetWellborePicksForPickIdentifierResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellborePicksForPickIdentifierResponse = - GetWellborePicksForPickIdentifierResponses[keyof GetWellborePicksForPickIdentifierResponses]; +export type GetWellborePicksForPickIdentifierResponse_api = + GetWellborePicksForPickIdentifierResponses_api[keyof GetWellborePicksForPickIdentifierResponses_api]; -export type GetWellborePicksForWellboreData = { +export type GetWellborePicksForWellboreData_api = { body?: never; path?: never; query: { @@ -2959,27 +2959,27 @@ export type GetWellborePicksForWellboreData = { url: "/well/wellbore_picks_for_wellbore/"; }; -export type GetWellborePicksForWellboreErrors = { +export type GetWellborePicksForWellboreErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetWellborePicksForWellboreError = - GetWellborePicksForWellboreErrors[keyof GetWellborePicksForWellboreErrors]; +export type GetWellborePicksForWellboreError_api = + GetWellborePicksForWellboreErrors_api[keyof GetWellborePicksForWellboreErrors_api]; -export type GetWellborePicksForWellboreResponses = { +export type GetWellborePicksForWellboreResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellborePicksForWellboreResponse = - GetWellborePicksForWellboreResponses[keyof GetWellborePicksForWellboreResponses]; +export type GetWellborePicksForWellboreResponse_api = + GetWellborePicksForWellboreResponses_api[keyof GetWellborePicksForWellboreResponses_api]; -export type GetWellborePicksInStratColumnData = { +export type GetWellborePicksInStratColumnData_api = { body?: never; path?: never; query: { @@ -2995,27 +2995,27 @@ export type GetWellborePicksInStratColumnData = { url: "/well/wellbore_picks_in_strat_column"; }; -export type GetWellborePicksInStratColumnErrors = { +export type GetWellborePicksInStratColumnErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetWellborePicksInStratColumnError = - GetWellborePicksInStratColumnErrors[keyof GetWellborePicksInStratColumnErrors]; +export type GetWellborePicksInStratColumnError_api = + GetWellborePicksInStratColumnErrors_api[keyof GetWellborePicksInStratColumnErrors_api]; -export type GetWellborePicksInStratColumnResponses = { +export type GetWellborePicksInStratColumnResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellborePicksInStratColumnResponse = - GetWellborePicksInStratColumnResponses[keyof GetWellborePicksInStratColumnResponses]; +export type GetWellborePicksInStratColumnResponse_api = + GetWellborePicksInStratColumnResponses_api[keyof GetWellborePicksInStratColumnResponses_api]; -export type GetWellboreCompletionsData = { +export type GetWellboreCompletionsData_api = { body?: never; path?: never; query: { @@ -3027,25 +3027,25 @@ export type GetWellboreCompletionsData = { url: "/well/wellbore_completions/"; }; -export type GetWellboreCompletionsErrors = { +export type GetWellboreCompletionsErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetWellboreCompletionsError = GetWellboreCompletionsErrors[keyof GetWellboreCompletionsErrors]; +export type GetWellboreCompletionsError_api = GetWellboreCompletionsErrors_api[keyof GetWellboreCompletionsErrors_api]; -export type GetWellboreCompletionsResponses = { +export type GetWellboreCompletionsResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellboreCompletionsResponse = GetWellboreCompletionsResponses[keyof GetWellboreCompletionsResponses]; +export type GetWellboreCompletionsResponse_api = GetWellboreCompletionsResponses_api[keyof GetWellboreCompletionsResponses_api]; -export type GetWellboreCasingsData = { +export type GetWellboreCasingsData_api = { body?: never; path?: never; query: { @@ -3057,25 +3057,25 @@ export type GetWellboreCasingsData = { url: "/well/wellbore_casings/"; }; -export type GetWellboreCasingsErrors = { +export type GetWellboreCasingsErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetWellboreCasingsError = GetWellboreCasingsErrors[keyof GetWellboreCasingsErrors]; +export type GetWellboreCasingsError_api = GetWellboreCasingsErrors_api[keyof GetWellboreCasingsErrors_api]; -export type GetWellboreCasingsResponses = { +export type GetWellboreCasingsResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellboreCasingsResponse = GetWellboreCasingsResponses[keyof GetWellboreCasingsResponses]; +export type GetWellboreCasingsResponse_api = GetWellboreCasingsResponses_api[keyof GetWellboreCasingsResponses_api]; -export type GetWellborePerforationsData = { +export type GetWellborePerforationsData_api = { body?: never; path?: never; query: { @@ -3087,25 +3087,25 @@ export type GetWellborePerforationsData = { url: "/well/wellbore_perforations/"; }; -export type GetWellborePerforationsErrors = { +export type GetWellborePerforationsErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetWellborePerforationsError = GetWellborePerforationsErrors[keyof GetWellborePerforationsErrors]; +export type GetWellborePerforationsError_api = GetWellborePerforationsErrors_api[keyof GetWellborePerforationsErrors_api]; -export type GetWellborePerforationsResponses = { +export type GetWellborePerforationsResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellborePerforationsResponse = GetWellborePerforationsResponses[keyof GetWellborePerforationsResponses]; +export type GetWellborePerforationsResponse_api = GetWellborePerforationsResponses_api[keyof GetWellborePerforationsResponses_api]; -export type GetWellboreLogCurveHeadersData = { +export type GetWellboreLogCurveHeadersData_api = { body?: never; path?: never; query: { @@ -3116,31 +3116,31 @@ export type GetWellboreLogCurveHeadersData = { /** * Sources to fetch well-logs from. */ - sources?: Array; + sources?: Array; }; url: "/well/wellbore_log_curve_headers/"; }; -export type GetWellboreLogCurveHeadersErrors = { +export type GetWellboreLogCurveHeadersErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetWellboreLogCurveHeadersError = GetWellboreLogCurveHeadersErrors[keyof GetWellboreLogCurveHeadersErrors]; +export type GetWellboreLogCurveHeadersError_api = GetWellboreLogCurveHeadersErrors_api[keyof GetWellboreLogCurveHeadersErrors_api]; -export type GetWellboreLogCurveHeadersResponses = { +export type GetWellboreLogCurveHeadersResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetWellboreLogCurveHeadersResponse = - GetWellboreLogCurveHeadersResponses[keyof GetWellboreLogCurveHeadersResponses]; +export type GetWellboreLogCurveHeadersResponse_api = + GetWellboreLogCurveHeadersResponses_api[keyof GetWellboreLogCurveHeadersResponses_api]; -export type GetLogCurveDataData = { +export type GetLogCurveDataData_api = { body?: never; path?: never; query: { @@ -3159,30 +3159,30 @@ export type GetLogCurveDataData = { /** * Source to fetch well-logs from. */ - source?: WellLogCurveSourceEnum; + source?: WellLogCurveSourceEnum_api; }; url: "/well/log_curve_data/"; }; -export type GetLogCurveDataErrors = { +export type GetLogCurveDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetLogCurveDataError = GetLogCurveDataErrors[keyof GetLogCurveDataErrors]; +export type GetLogCurveDataError_api = GetLogCurveDataErrors_api[keyof GetLogCurveDataErrors_api]; -export type GetLogCurveDataResponses = { +export type GetLogCurveDataResponses_api = { /** * Successful Response */ - 200: WellboreLogCurveData; + 200: WellboreLogCurveData_api; }; -export type GetLogCurveDataResponse = GetLogCurveDataResponses[keyof GetLogCurveDataResponses]; +export type GetLogCurveDataResponse_api = GetLogCurveDataResponses_api[keyof GetLogCurveDataResponses_api]; -export type GetSeismicCubeMetaListData = { +export type GetSeismicCubeMetaListData_api = { body?: never; path?: never; query: { @@ -3198,25 +3198,25 @@ export type GetSeismicCubeMetaListData = { url: "/seismic/seismic_cube_meta_list/"; }; -export type GetSeismicCubeMetaListErrors = { +export type GetSeismicCubeMetaListErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetSeismicCubeMetaListError = GetSeismicCubeMetaListErrors[keyof GetSeismicCubeMetaListErrors]; +export type GetSeismicCubeMetaListError_api = GetSeismicCubeMetaListErrors_api[keyof GetSeismicCubeMetaListErrors_api]; -export type GetSeismicCubeMetaListResponses = { +export type GetSeismicCubeMetaListResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetSeismicCubeMetaListResponse = GetSeismicCubeMetaListResponses[keyof GetSeismicCubeMetaListResponses]; +export type GetSeismicCubeMetaListResponse_api = GetSeismicCubeMetaListResponses_api[keyof GetSeismicCubeMetaListResponses_api]; -export type GetInlineSliceData = { +export type GetInlineSliceData_api = { body?: never; path?: never; query: { @@ -3252,25 +3252,25 @@ export type GetInlineSliceData = { url: "/seismic/get_inline_slice/"; }; -export type GetInlineSliceErrors = { +export type GetInlineSliceErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetInlineSliceError = GetInlineSliceErrors[keyof GetInlineSliceErrors]; +export type GetInlineSliceError_api = GetInlineSliceErrors_api[keyof GetInlineSliceErrors_api]; -export type GetInlineSliceResponses = { +export type GetInlineSliceResponses_api = { /** * Successful Response */ - 200: SeismicSliceData; + 200: SeismicSliceData_api; }; -export type GetInlineSliceResponse = GetInlineSliceResponses[keyof GetInlineSliceResponses]; +export type GetInlineSliceResponse_api = GetInlineSliceResponses_api[keyof GetInlineSliceResponses_api]; -export type GetCrosslineSliceData = { +export type GetCrosslineSliceData_api = { body?: never; path?: never; query: { @@ -3306,25 +3306,25 @@ export type GetCrosslineSliceData = { url: "/seismic/get_crossline_slice/"; }; -export type GetCrosslineSliceErrors = { +export type GetCrosslineSliceErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetCrosslineSliceError = GetCrosslineSliceErrors[keyof GetCrosslineSliceErrors]; +export type GetCrosslineSliceError_api = GetCrosslineSliceErrors_api[keyof GetCrosslineSliceErrors_api]; -export type GetCrosslineSliceResponses = { +export type GetCrosslineSliceResponses_api = { /** * Successful Response */ - 200: SeismicSliceData; + 200: SeismicSliceData_api; }; -export type GetCrosslineSliceResponse = GetCrosslineSliceResponses[keyof GetCrosslineSliceResponses]; +export type GetCrosslineSliceResponse_api = GetCrosslineSliceResponses_api[keyof GetCrosslineSliceResponses_api]; -export type GetDepthSliceData = { +export type GetDepthSliceData_api = { body?: never; path?: never; query: { @@ -3360,26 +3360,26 @@ export type GetDepthSliceData = { url: "/seismic/get_depth_slice/"; }; -export type GetDepthSliceErrors = { +export type GetDepthSliceErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetDepthSliceError = GetDepthSliceErrors[keyof GetDepthSliceErrors]; +export type GetDepthSliceError_api = GetDepthSliceErrors_api[keyof GetDepthSliceErrors_api]; -export type GetDepthSliceResponses = { +export type GetDepthSliceResponses_api = { /** * Successful Response */ - 200: SeismicSliceData; + 200: SeismicSliceData_api; }; -export type GetDepthSliceResponse = GetDepthSliceResponses[keyof GetDepthSliceResponses]; +export type GetDepthSliceResponse_api = GetDepthSliceResponses_api[keyof GetDepthSliceResponses_api]; -export type PostGetSeismicFenceData = { - body: BodyPostGetSeismicFence; +export type PostGetSeismicFenceData_api = { + body: BodyPostGetSeismicFence_api; path?: never; query: { /** @@ -3410,25 +3410,25 @@ export type PostGetSeismicFenceData = { url: "/seismic/get_seismic_fence/"; }; -export type PostGetSeismicFenceErrors = { +export type PostGetSeismicFenceErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type PostGetSeismicFenceError = PostGetSeismicFenceErrors[keyof PostGetSeismicFenceErrors]; +export type PostGetSeismicFenceError_api = PostGetSeismicFenceErrors_api[keyof PostGetSeismicFenceErrors_api]; -export type PostGetSeismicFenceResponses = { +export type PostGetSeismicFenceResponses_api = { /** * Successful Response */ - 200: SeismicFenceData; + 200: SeismicFenceData_api; }; -export type PostGetSeismicFenceResponse = PostGetSeismicFenceResponses[keyof PostGetSeismicFenceResponses]; +export type PostGetSeismicFenceResponse_api = PostGetSeismicFenceResponses_api[keyof PostGetSeismicFenceResponses_api]; -export type GetPolygonsDirectoryData = { +export type GetPolygonsDirectoryData_api = { body?: never; path?: never; query: { @@ -3444,25 +3444,25 @@ export type GetPolygonsDirectoryData = { url: "/polygons/polygons_directory/"; }; -export type GetPolygonsDirectoryErrors = { +export type GetPolygonsDirectoryErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetPolygonsDirectoryError = GetPolygonsDirectoryErrors[keyof GetPolygonsDirectoryErrors]; +export type GetPolygonsDirectoryError_api = GetPolygonsDirectoryErrors_api[keyof GetPolygonsDirectoryErrors_api]; -export type GetPolygonsDirectoryResponses = { +export type GetPolygonsDirectoryResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetPolygonsDirectoryResponse = GetPolygonsDirectoryResponses[keyof GetPolygonsDirectoryResponses]; +export type GetPolygonsDirectoryResponse_api = GetPolygonsDirectoryResponses_api[keyof GetPolygonsDirectoryResponses_api]; -export type GetPolygonsDataData = { +export type GetPolygonsDataData_api = { body?: never; path?: never; query: { @@ -3490,25 +3490,25 @@ export type GetPolygonsDataData = { url: "/polygons/polygons_data/"; }; -export type GetPolygonsDataErrors = { +export type GetPolygonsDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetPolygonsDataError = GetPolygonsDataErrors[keyof GetPolygonsDataErrors]; +export type GetPolygonsDataError_api = GetPolygonsDataErrors_api[keyof GetPolygonsDataErrors_api]; -export type GetPolygonsDataResponses = { +export type GetPolygonsDataResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetPolygonsDataResponse = GetPolygonsDataResponses[keyof GetPolygonsDataResponses]; +export type GetPolygonsDataResponse_api = GetPolygonsDataResponses_api[keyof GetPolygonsDataResponses_api]; -export type GetUserPhotoData = { +export type GetUserPhotoData_api = { body?: never; path?: never; query: { @@ -3520,25 +3520,25 @@ export type GetUserPhotoData = { url: "/graph/user_photo/"; }; -export type GetUserPhotoErrors = { +export type GetUserPhotoErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetUserPhotoError = GetUserPhotoErrors[keyof GetUserPhotoErrors]; +export type GetUserPhotoError_api = GetUserPhotoErrors_api[keyof GetUserPhotoErrors_api]; -export type GetUserPhotoResponses = { +export type GetUserPhotoResponses_api = { /** * Successful Response */ - 200: GraphUserPhoto; + 200: GraphUserPhoto_api; }; -export type GetUserPhotoResponse = GetUserPhotoResponses[keyof GetUserPhotoResponses]; +export type GetUserPhotoResponse_api = GetUserPhotoResponses_api[keyof GetUserPhotoResponses_api]; -export type GetObservationsData = { +export type GetObservationsData_api = { body?: never; path?: never; query: { @@ -3550,25 +3550,25 @@ export type GetObservationsData = { url: "/observations/observations/"; }; -export type GetObservationsErrors = { +export type GetObservationsErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetObservationsError = GetObservationsErrors[keyof GetObservationsErrors]; +export type GetObservationsError_api = GetObservationsErrors_api[keyof GetObservationsErrors_api]; -export type GetObservationsResponses = { +export type GetObservationsResponses_api = { /** * Successful Response */ - 200: Observations; + 200: Observations_api; }; -export type GetObservationsResponse = GetObservationsResponses[keyof GetObservationsResponses]; +export type GetObservationsResponse_api = GetObservationsResponses_api[keyof GetObservationsResponses_api]; -export type GetTableDefinitionData = { +export type GetTableDefinitionData_api = { body?: never; path?: never; query: { @@ -3584,25 +3584,25 @@ export type GetTableDefinitionData = { url: "/rft/table_definition"; }; -export type GetTableDefinitionErrors = { +export type GetTableDefinitionErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetTableDefinitionError = GetTableDefinitionErrors[keyof GetTableDefinitionErrors]; +export type GetTableDefinitionError_api = GetTableDefinitionErrors_api[keyof GetTableDefinitionErrors_api]; -export type GetTableDefinitionResponses = { +export type GetTableDefinitionResponses_api = { /** * Successful Response */ - 200: RftTableDefinition; + 200: RftTableDefinition_api; }; -export type GetTableDefinitionResponse = GetTableDefinitionResponses[keyof GetTableDefinitionResponses]; +export type GetTableDefinitionResponse_api = GetTableDefinitionResponses_api[keyof GetTableDefinitionResponses_api]; -export type GetRealizationDataData = { +export type GetRealizationDataData_api = { body?: never; path?: never; query: { @@ -3634,25 +3634,25 @@ export type GetRealizationDataData = { url: "/rft/realization_data"; }; -export type GetRealizationDataErrors = { +export type GetRealizationDataErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetRealizationDataError = GetRealizationDataErrors[keyof GetRealizationDataErrors]; +export type GetRealizationDataError_api = GetRealizationDataErrors_api[keyof GetRealizationDataErrors_api]; -export type GetRealizationDataResponses = { +export type GetRealizationDataResponses_api = { /** * Successful Response */ - 200: Array; + 200: Array; }; -export type GetRealizationDataResponse = GetRealizationDataResponses[keyof GetRealizationDataResponses]; +export type GetRealizationDataResponse_api = GetRealizationDataResponses_api[keyof GetRealizationDataResponses_api]; -export type GetVfpTableNamesData = { +export type GetVfpTableNamesData_api = { body?: never; path?: never; query: { @@ -3672,25 +3672,25 @@ export type GetVfpTableNamesData = { url: "/vfp/vfp_table_names/"; }; -export type GetVfpTableNamesErrors = { +export type GetVfpTableNamesErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetVfpTableNamesError = GetVfpTableNamesErrors[keyof GetVfpTableNamesErrors]; +export type GetVfpTableNamesError_api = GetVfpTableNamesErrors_api[keyof GetVfpTableNamesErrors_api]; -export type GetVfpTableNamesResponses = { +export type GetVfpTableNamesResponses_api = { /** * Successful Response */ 200: Array; }; -export type GetVfpTableNamesResponse = GetVfpTableNamesResponses[keyof GetVfpTableNamesResponses]; +export type GetVfpTableNamesResponse_api = GetVfpTableNamesResponses_api[keyof GetVfpTableNamesResponses_api]; -export type GetVfpTableData = { +export type GetVfpTableData_api = { body?: never; path?: never; query: { @@ -3714,25 +3714,25 @@ export type GetVfpTableData = { url: "/vfp/vfp_table/"; }; -export type GetVfpTableErrors = { +export type GetVfpTableErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetVfpTableError = GetVfpTableErrors[keyof GetVfpTableErrors]; +export type GetVfpTableError_api = GetVfpTableErrors_api[keyof GetVfpTableErrors_api]; -export type GetVfpTableResponses = { +export type GetVfpTableResponses_api = { /** * Successful Response */ - 200: VfpProdTable | VfpInjTable; + 200: VfpProdTable_api | VfpInjTable_api; }; -export type GetVfpTableResponse = GetVfpTableResponses[keyof GetVfpTableResponses]; +export type GetVfpTableResponse_api = GetVfpTableResponses_api[keyof GetVfpTableResponses_api]; -export type LoginRouteData = { +export type LoginRouteData_api = { body?: never; path?: never; query?: { @@ -3741,85 +3741,85 @@ export type LoginRouteData = { url: "/login"; }; -export type LoginRouteErrors = { +export type LoginRouteErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type LoginRouteError = LoginRouteErrors[keyof LoginRouteErrors]; +export type LoginRouteError_api = LoginRouteErrors_api[keyof LoginRouteErrors_api]; -export type LoginRouteResponses = { +export type LoginRouteResponses_api = { /** * Successful Response */ 200: unknown; }; -export type AuthorizedCallbackRouteData = { +export type AuthorizedCallbackRouteData_api = { body?: never; path?: never; query?: never; url: "/auth-callback"; }; -export type AuthorizedCallbackRouteResponses = { +export type AuthorizedCallbackRouteResponses_api = { /** * Successful Response */ 200: unknown; }; -export type GetAliveData = { +export type GetAliveData_api = { body?: never; path?: never; query?: never; url: "/alive"; }; -export type GetAliveResponses = { +export type GetAliveResponses_api = { /** * Successful Response */ 200: string; }; -export type GetAliveResponse = GetAliveResponses[keyof GetAliveResponses]; +export type GetAliveResponse_api = GetAliveResponses_api[keyof GetAliveResponses_api]; -export type GetAliveProtectedData = { +export type GetAliveProtectedData_api = { body?: never; path?: never; query?: never; url: "/alive_protected"; }; -export type GetAliveProtectedResponses = { +export type GetAliveProtectedResponses_api = { /** * Successful Response */ 200: string; }; -export type GetAliveProtectedResponse = GetAliveProtectedResponses[keyof GetAliveProtectedResponses]; +export type GetAliveProtectedResponse_api = GetAliveProtectedResponses_api[keyof GetAliveProtectedResponses_api]; -export type PostLogoutData = { +export type PostLogoutData_api = { body?: never; path?: never; query?: never; url: "/logout"; }; -export type PostLogoutResponses = { +export type PostLogoutResponses_api = { /** * Successful Response */ 200: string; }; -export type PostLogoutResponse = PostLogoutResponses[keyof PostLogoutResponses]; +export type PostLogoutResponse_api = PostLogoutResponses_api[keyof PostLogoutResponses_api]; -export type GetLoggedInUserData = { +export type GetLoggedInUserData_api = { body?: never; path?: never; query?: { @@ -3831,36 +3831,36 @@ export type GetLoggedInUserData = { url: "/logged_in_user"; }; -export type GetLoggedInUserErrors = { +export type GetLoggedInUserErrors_api = { /** * Validation Error */ - 422: HttpValidationError; + 422: HttpValidationError_api; }; -export type GetLoggedInUserError = GetLoggedInUserErrors[keyof GetLoggedInUserErrors]; +export type GetLoggedInUserError_api = GetLoggedInUserErrors_api[keyof GetLoggedInUserErrors_api]; -export type GetLoggedInUserResponses = { +export type GetLoggedInUserResponses_api = { /** * Successful Response */ - 200: UserInfo; + 200: UserInfo_api; }; -export type GetLoggedInUserResponse = GetLoggedInUserResponses[keyof GetLoggedInUserResponses]; +export type GetLoggedInUserResponse_api = GetLoggedInUserResponses_api[keyof GetLoggedInUserResponses_api]; -export type RootData = { +export type RootData_api = { body?: never; path?: never; query?: never; url: "/"; }; -export type RootResponses = { +export type RootResponses_api = { /** * Successful Response */ 200: string; }; -export type RootResponse = RootResponses[keyof RootResponses]; +export type RootResponse_api = RootResponses_api[keyof RootResponses_api]; diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts index 915429d78..bd0bb6d08 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts @@ -77,6 +77,11 @@ export class ExternalSettingController< } } + if (this._controlledSettings.size === 0) { + this._setting.setAvailableValues(null); + return; + } + this.makeIntersectionOfAvailableValues(); } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts index 4779c33e6..b67192f15 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts @@ -6,7 +6,6 @@ import type { WorkbenchSettings } from "@framework/WorkbenchSettings"; import type { PublishSubscribe } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { PublishSubscribeDelegate } from "@modules/_shared/utils/PublishSubscribeDelegate"; - import { UnsubscribeHandlerDelegate } from "../../delegates/UnsubscribeHandlerDelegate"; import type { CustomSettingImplementation } from "../../interfacesAndTypes/customSettingImplementation"; import type { SettingAttributes } from "../../interfacesAndTypes/customSettingsHandler"; @@ -133,51 +132,57 @@ export class SettingManager< ); this._unsubscribeHandler.registerUnsubscribeFunction( "external-setting-controller", - externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_LOADING)( - () => { - this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_LOADING); - } - ), + externalController + .getSetting() + .getPublishSubscribeDelegate() + .makeSubscriberFunction(SettingTopic.IS_LOADING)(() => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_LOADING); + }), ); this._unsubscribeHandler.registerUnsubscribeFunction( "external-setting-controller", - externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.ATTRIBUTES)( - () => { - this._publishSubscribeDelegate.notifySubscribers(SettingTopic.ATTRIBUTES); - } - ), + externalController + .getSetting() + .getPublishSubscribeDelegate() + .makeSubscriberFunction(SettingTopic.ATTRIBUTES)(() => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.ATTRIBUTES); + }), ); this._unsubscribeHandler.registerUnsubscribeFunction( "external-setting-controller", - externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.VALUE_ABOUT_TO_BE_CHANGED)( - () => { - this._publishSubscribeDelegate.notifySubscribers(SettingTopic.VALUE_ABOUT_TO_BE_CHANGED); - } - ), + externalController + .getSetting() + .getPublishSubscribeDelegate() + .makeSubscriberFunction(SettingTopic.VALUE_ABOUT_TO_BE_CHANGED)(() => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.VALUE_ABOUT_TO_BE_CHANGED); + }), ); this._unsubscribeHandler.registerUnsubscribeFunction( "external-setting-controller", - externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_INITIALIZED)( - () => { - this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_INITIALIZED); - } - ), + externalController + .getSetting() + .getPublishSubscribeDelegate() + .makeSubscriberFunction(SettingTopic.IS_INITIALIZED)(() => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_INITIALIZED); + }), ); this._unsubscribeHandler.registerUnsubscribeFunction( "external-setting-controller", - externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_PERSISTED)( - () => { - this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_PERSISTED); - } - ), + externalController + .getSetting() + .getPublishSubscribeDelegate() + .makeSubscriberFunction(SettingTopic.IS_PERSISTED)(() => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_PERSISTED); + }), ); this._unsubscribeHandler.registerUnsubscribeFunction( "external-setting-controller", - externalController.getSetting().getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.AVAILABLE_VALUES)( - () => { - this._publishSubscribeDelegate.notifySubscribers(SettingTopic.AVAILABLE_VALUES); - } - ), + externalController + .getSetting() + .getPublishSubscribeDelegate() + .makeSubscriberFunction(SettingTopic.AVAILABLE_VALUES)(() => { + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.AVAILABLE_VALUES); + }), ); } @@ -332,7 +337,9 @@ export class SettingManager< workbenchSettings: WorkbenchSettings, ): React.ReactNode { if (this._externalController) { - return this._externalController.getSetting().valueToRepresentation(value, workbenchSession, workbenchSettings); + return this._externalController + .getSetting() + .valueToRepresentation(value, workbenchSession, workbenchSettings); } if (this._customSettingImplementation.overriddenValueRepresentation) { @@ -371,7 +378,7 @@ export class SettingManager< : OverriddenValueProviderType.SHARED_SETTING; } return externalController.getSetting().makeSnapshotGetter(topic)(); - } + }; } const snapshotGetter = (): any => { @@ -454,12 +461,12 @@ export class SettingManager< return false; } - setAvailableValues(availableValues: MakeAvailableValuesTypeBasedOnCategory): void { + setAvailableValues(availableValues: MakeAvailableValuesTypeBasedOnCategory | null): void { if (this._externalController) { this.initialize(); this._externalController.setAvailableValues(availableValues); } - + if (isEqual(this._availableValues, availableValues) && this._initialized) { this.setLoading(false); return; diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx index e8bb3cf48..03694d506 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx @@ -5,7 +5,6 @@ import { Link, Warning } from "@mui/icons-material"; import { PendingWrapper } from "@lib/components/PendingWrapper"; import { resolveClassNames } from "@lib/utils/resolveClassNames"; - import { usePublishSubscribeTopicValue } from "../../../utils/PublishSubscribeDelegate"; import type { SettingComponentProps as SettingComponentPropsInterface } from "../../interfacesAndTypes/customSettingImplementation"; import type { Setting, SettingCategories, SettingTypes } from "../../settings/settingsDefinitions"; From 84f109285d835e15fe282a2fc1deba77b9da38a2 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 2 May 2025 15:53:18 +0200 Subject: [PATCH 76/97] wip --- .../framework/DataProvider/DataProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts index c6d70e24e..f9d0bafbe 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts @@ -393,7 +393,7 @@ export class DataProvider< if (apiError) { this._error = apiError.makeStatusMessage(); } else { - this._error = "An error occurred"; + this._error = error.message; } this.setStatus(DataProviderStatus.ERROR); } From 2cc806ec5206d8e0dc452cfe99cee94861f2cc19 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 5 May 2025 16:05:56 +0200 Subject: [PATCH 77/97] Adjustments to api types --- frontend/src/api/autogen/types.gen.ts | 68 ++++++++++++++------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/frontend/src/api/autogen/types.gen.ts b/frontend/src/api/autogen/types.gen.ts index 49a900037..fe29e8548 100644 --- a/frontend/src/api/autogen/types.gen.ts +++ b/frontend/src/api/autogen/types.gen.ts @@ -133,16 +133,16 @@ export type EnsembleParameter_api = { is_logarithmic: boolean; is_discrete: boolean; is_constant: boolean; - group_name: string | null; - descriptive_name: string | null; + group_name?: string | null; + descriptive_name?: string | null; realizations: Array; values: Array | Array | Array; }; export type EnsembleParameterDescription_api = { name: string; - group_name: string | null; - descriptive_name: string | null; + group_name?: string | null; + descriptive_name?: string | null; is_discrete: boolean; }; @@ -152,8 +152,8 @@ export type EnsembleParameterDescription_api = { export type EnsembleScalarResponse_api = { realizations: Array; values: Array; - name: string | null; - unit: string | null; + name?: string | null; + unit?: string | null; }; /** @@ -198,7 +198,7 @@ export type FlowNetworkData_api = { export type FlowNetworkMetadata_api = { key: string; label: string; - unit: string | null; + unit?: string | null; }; export enum FlowRateType_api { @@ -232,7 +232,7 @@ export enum Gfr_api { } export type GraphUserPhoto_api = { - avatar_b64str: string | null; + avatar_b64str?: string | null; }; /** @@ -280,7 +280,7 @@ export type Grid3dMappedProperty_api = { */ export type Grid3dPropertyInfo_api = { property_name: string; - iso_date_or_interval: string | null; + iso_date_or_interval?: string | null; }; /** @@ -425,8 +425,8 @@ export enum NodeType_api { * A collection of observations associated with a field/case/ensemble */ export type Observations_api = { - summary: Array; - rft: Array; + summary?: Array; + rft?: Array; }; export type PointSetXy_api = { @@ -462,9 +462,9 @@ export enum PolygonsAttributeType_api { export type PolygonsMeta_api = { name: string; name_is_stratigraphic_offical: boolean; - stratigraphic_identifier: string | null; - relative_stratigraphic_level: number | null; - parent_stratigraphic_identifier: string | null; + stratigraphic_identifier?: string | null; + relative_stratigraphic_level?: number | null; + parent_stratigraphic_identifier?: string | null; attribute_name: string; attribute_type: PolygonsAttributeType_api; }; @@ -521,7 +521,7 @@ export type RepeatedTableColumnData_api = { */ export type RftObservation_api = { value: number; - comment: string | null; + comment?: string | null; error: number; zone: string; md_msl: number; @@ -542,7 +542,7 @@ export type RftObservation_api = { export type RftObservations_api = { well: string; date: string; - comment: string | null; + comment?: string | null; observations: Array; }; @@ -741,7 +741,7 @@ export type StratigraphicUnit_api = { colorR: number; colorG: number; colorB: number; - lithologyType: number | number | string; + lithologyType?: number | number | string; }; /** @@ -749,7 +749,7 @@ export type StratigraphicUnit_api = { */ export type SummaryVectorDateObservation_api = { date: string; - comment: string | null; + comment?: string | null; value: number; error: number; label: string; @@ -760,7 +760,7 @@ export type SummaryVectorDateObservation_api = { */ export type SummaryVectorObservations_api = { vector_name: string; - comment: string | null; + comment?: string | null; observations: Array; }; @@ -790,7 +790,7 @@ export enum SurfaceAttributeType_api { } export type SurfaceDataFloat_api = { - format: "float"; + format?: "float"; surface_def: SurfaceDef_api; transformed_bbox_utm: BoundingBox2D_api; value_min: number; @@ -799,7 +799,7 @@ export type SurfaceDataFloat_api = { }; export type SurfaceDataPng_api = { - format: "png"; + format?: "png"; surface_def: SurfaceDef_api; transformed_bbox_utm: BoundingBox2D_api; value_min: number; @@ -891,7 +891,9 @@ export enum SurfaceTimeType_api { INTERVAL = "INTERVAL", } -export type Thp_api = "THP"; +export enum Thp_api { + THP = "THP", +} export enum TabType_api { BHP = "BHP", @@ -930,8 +932,8 @@ export enum UnitType_api { export type UserInfo_api = { username: string; - display_name: string | null; - avatar_b64str: string | null; + display_name?: string | null; + avatar_b64str?: string | null; has_sumo_access: boolean; has_smda_access: boolean; }; @@ -946,7 +948,7 @@ export type VectorDescription_api = { name: string; descriptiveName: string; hasHistorical: boolean; - derivedVectorInfo: DerivedVectorInfo_api | null; + derivedVectorInfo?: DerivedVectorInfo_api | null; }; export type VectorHistoricalData_api = { @@ -962,7 +964,7 @@ export type VectorRealizationData_api = { values: Array; unit: string; isRate: boolean; - derivedVectorInfo: DerivedVectorInfo_api | null; + derivedVectorInfo?: DerivedVectorInfo_api | null; }; export type VectorStatisticData_api = { @@ -971,7 +973,7 @@ export type VectorStatisticData_api = { valueObjects: Array; unit: string; isRate: boolean; - derivedVectorInfo: DerivedVectorInfo_api | null; + derivedVectorInfo?: DerivedVectorInfo_api | null; }; export type VectorStatisticSensitivityData_api = { @@ -985,7 +987,7 @@ export type VectorStatisticSensitivityData_api = { }; export type VfpInjTable_api = { - vfpType: "INJ"; + vfpType?: "INJ"; tableNumber: number; datum: number; flowRateType: FlowRateType_api; @@ -1000,7 +1002,7 @@ export type VfpInjTable_api = { }; export type VfpProdTable_api = { - vfpType: "PROD"; + vfpType?: "PROD"; tableNumber: number; datum: number; flowRateType: FlowRateType_api; @@ -1064,7 +1066,7 @@ export type WellCompletionsWell_api = { export type WellCompletionsZone_api = { name: string; - subzones: Array | null; + subzones?: Array | null; }; export enum WellLogCurveSourceEnum_api { @@ -1083,8 +1085,8 @@ export type WellboreCasing_api = { itemType: string; diameterNumeric: number; diameterInner: number; - description: string | null; - remark: string | null; + description?: string | null; + remark?: string | null; depthTopMd: number; depthBottomMd: number; totalDepthMd: number; @@ -1165,7 +1167,7 @@ export type WellborePick_api = { uniqueWellboreIdentifier: string; wellboreUuid: string; pickIdentifier: string; - confidence: string | null; + confidence?: string | null; depthReferencePoint: string; mdUnit: string; interpreter: string | null; From 967772fa84b439b21bfe95e347d45dbc8219734f Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 5 May 2025 16:09:19 +0200 Subject: [PATCH 78/97] Minor fixes --- .../delegates/SettingsContextDelegate.ts | 34 +++++++++---------- .../framework/DataProvider/DataProvider.ts | 1 - 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts index 9209c8323..f6ff27a25 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts @@ -1,7 +1,5 @@ - import type { PublishSubscribe } from "../../utils/PublishSubscribeDelegate"; import { PublishSubscribeDelegate } from "../../utils/PublishSubscribeDelegate"; -import type { DataProvider } from "../framework/DataProvider/DataProvider"; import { type DataProviderManager, DataProviderManagerTopic, @@ -25,8 +23,8 @@ export enum SettingsContextStatus { } export enum SettingsContextDelegateTopic { - SETTINGS_AND_STORED_DATA_CHANGED = "SETTINGS_CHANGED", - STATUS = "LOADING_STATE_CHANGED", + SETTINGS_AND_STORED_DATA_CHANGED = "SETTINGS_AND_STORED_DATA_CHANGED", + STATUS = "STATUS", } export type SettingsContextDelegatePayloads = { @@ -58,7 +56,6 @@ export class SettingsContextDelegate< TStoredDataKey extends keyof TStoredData = keyof TStoredData, > implements PublishSubscribe { - private _owner: DataProvider; private _customSettingsHandler: CustomSettingsHandler< TSettings, TStoredData, @@ -80,7 +77,6 @@ export class SettingsContextDelegate< private _dependencies: Dependency[] = []; constructor( - owner: DataProvider, customSettingsHandler: CustomSettingsHandler< TSettings, TStoredData, @@ -91,7 +87,6 @@ export class SettingsContextDelegate< dataProviderManager: DataProviderManager, settings: { [K in TSettingKey]: SettingManager }, ) { - this._owner = owner; this._customSettingsHandler = customSettingsHandler; this._dataProviderManager = dataProviderManager; @@ -100,8 +95,8 @@ export class SettingsContextDelegate< this.getDataProviderManager() .getPublishSubscribeDelegate() .makeSubscriberFunction(DataProviderManagerTopic.GLOBAL_SETTINGS)(() => { - this.handleSettingChanged(); - }), + this.handleSettingChanged(); + }), ); for (const key in settings) { @@ -305,11 +300,12 @@ export class SettingsContextDelegate< this._unsubscribeHandler.registerUnsubscribeFunction( "dependencies", - this._settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_LOADING)(() => { - if (!this._settings[key].isLoading()) { - handleChange(); - } - } + this._settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_LOADING)( + () => { + if (!this._settings[key].isLoading()) { + handleChange(); + } + }, ), ); @@ -342,7 +338,7 @@ export class SettingsContextDelegate< const loadingStateGetter = (settingKey: K): boolean => { return this._settings[settingKey].isLoading(); - } + }; const availableSettingsUpdater = ( settingKey: K, @@ -419,7 +415,7 @@ export class SettingsContextDelegate< TSettings, TSettingTypes, TSettingKey - >(this, updateFunc, makeLocalSettingGetter,loadingStateGetter, makeGlobalSettingGetter); + >(this, updateFunc, makeLocalSettingGetter, loadingStateGetter, makeGlobalSettingGetter); this._dependencies.push(dependency); dependency.subscribe((storedData: TStoredData[K] | null) => { @@ -506,7 +502,11 @@ export class SettingsContextDelegate< return; } - if (this.isSomePersistedSettingNotValid() || !this.areCurrentSettingsValid() || !this.areAllSettingsInitialized()) { + if ( + this.isSomePersistedSettingNotValid() || + !this.areCurrentSettingsValid() || + !this.areAllSettingsInitialized() + ) { this.setStatus(SettingsContextStatus.INVALID_SETTINGS); return; } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts index f9d0bafbe..e11100170 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts @@ -132,7 +132,6 @@ export class DataProvider< this._type = type; this._dataProviderManager = dataProviderManager; this._settingsContextDelegate = new SettingsContextDelegate( - this, customDataProviderImplementation, dataProviderManager, makeSettings( From 6c826e0a63cd7c3586c1874c7e2e0842e4c81d97 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 6 May 2025 16:14:21 +0200 Subject: [PATCH 79/97] fixes --- .../delegates/SettingsContextDelegate.ts | 22 +++++++------------ .../SettingManager/SettingManager.ts | 4 ++++ .../settings/settingsDefinitions.ts | 6 ++--- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts index f6ff27a25..991384369 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts @@ -367,13 +367,6 @@ export class SettingsContextDelegate< this._settings[settingKey].setLoading(loading); } this.handleSettingChanged(); - /* - this._settings[settingKey].setLoading(loading); - - if (!hasDependencies && !loading) { - this.handleSettingChanged(); - } - */ }); dependency.initialize(); @@ -428,13 +421,6 @@ export class SettingsContextDelegate< this._storedDataLoadingStatus[key] = loading; this.handleSettingChanged(); } - /* - this._settings[settingKey].setLoading(loading); - - if (!hasDependencies && !loading) { - this.handleSettingChanged(); - } - */ }); dependency.initialize(); @@ -485,6 +471,14 @@ export class SettingsContextDelegate< beforeDestroy(): void { this._unsubscribeHandler.unsubscribeAll(); + for (const dependency of this._dependencies) { + dependency.beforeDestroy(); + } + this._dependencies = []; + for (const key in this._settings) { + this._settings[key].beforeDestroy(); + } + this._settings = {} as { [K in TSettingKey]: SettingManager }; } private setStatus(status: SettingsContextStatus) { diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts index b67192f15..0aadc7194 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts @@ -191,6 +191,10 @@ export class SettingManager< this._unsubscribeHandler.unsubscribe("external-setting-controller"); } + beforeDestroy(): void { + this._unsubscribeHandler.unsubscribeAll(); + } + getId(): string { return this._id; } diff --git a/frontend/src/modules/_shared/DataProviderFramework/settings/settingsDefinitions.ts b/frontend/src/modules/_shared/DataProviderFramework/settings/settingsDefinitions.ts index 1907cf5f9..547903977 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/settings/settingsDefinitions.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/settings/settingsDefinitions.ts @@ -5,13 +5,11 @@ import type { ColorScaleSpecification } from "@framework/components/ColorScaleSe import type { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; import type { ColorSet } from "@lib/utils/ColorSet"; - import type { AvailableValuesType } from "../interfacesAndTypes/utils"; import type { IntersectionSettingValue } from "./implementations/IntersectionSetting"; import type { SensitivityNameCasePair } from "./implementations/SensitivitySetting"; - export enum SettingCategory { SINGLE_SELECT = "singleSelect", MULTI_SELECT = "multiSelect", @@ -308,7 +306,7 @@ export const settingCategoryAvailableValuesIntersectionReducerMap: SettingCatego return [Math.max(min, currentMin), Math.min(max, currentMax)]; }, - startingValue: [Number.MIN_VALUE, Number.MAX_VALUE], + startingValue: [-Number.MAX_VALUE, Number.MAX_VALUE], }, [SettingCategory.RANGE]: { reducer: (accumulator, currentAvailableValues) => { @@ -317,7 +315,7 @@ export const settingCategoryAvailableValuesIntersectionReducerMap: SettingCatego return [Math.max(min, currentMin), Math.min(max, currentMax)]; }, - startingValue: [Number.MIN_VALUE, Number.MAX_VALUE], + startingValue: [-Number.MAX_VALUE, Number.MAX_VALUE], }, }; From dcf0e0b9f324ec3eb8c0b31605bc42e7dbdfed4a Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 7 May 2025 10:18:42 +0200 Subject: [PATCH 80/97] fixes --- .../delegates/GroupDelegate.ts | 8 +++++++- .../delegates/SharedSettingsDelegate.ts | 15 ++++++++++++--- .../delegates/_utils/Dependency.ts | 13 +++++++++---- .../DataProviderManager/DataProviderManager.ts | 2 +- .../ExternalSettingController.ts | 5 +++++ .../framework/Group/Group.ts | 5 +++++ .../framework/SettingsGroup/SettingsGroup.ts | 4 ++++ .../framework/SharedSetting/SharedSetting.ts | 1 + .../interfacesAndTypes/entities.ts | 1 - 9 files changed, 44 insertions(+), 10 deletions(-) diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/GroupDelegate.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/GroupDelegate.ts index e6ac1fe27..863b9a3c5 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/GroupDelegate.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/GroupDelegate.ts @@ -1,4 +1,3 @@ - import type { PublishSubscribe } from "../../utils/PublishSubscribeDelegate"; import { PublishSubscribeDelegate } from "../../utils/PublishSubscribeDelegate"; import { DataProvider } from "../framework/DataProvider/DataProvider"; @@ -315,4 +314,11 @@ export class GroupDelegate implements PublishSubscribe = SettingsKeysFromTuple, > { - private _parentItem: Item; private _externalSettingControllers: { [K in TSettingKey]: ExternalSettingController } = {} as { [K in TSettingKey]: ExternalSettingController; }; @@ -21,7 +19,6 @@ export class SharedSettingsDelegate< private _unsubscribeHandler: UnsubscribeHandlerDelegate = new UnsubscribeHandlerDelegate(); constructor(parentItem: Item, wrappedSettings: { [K in TSettingKey]: SettingManager }) { - this._parentItem = parentItem; this._wrappedSettings = wrappedSettings; for (const key in wrappedSettings) { @@ -43,4 +40,16 @@ export class SharedSettingsDelegate< unsubscribeAll(): void { this._unsubscribeHandler.unsubscribeAll(); } + + beforeDestroy(): void { + this._unsubscribeHandler.unsubscribeAll(); + for (const key in this._externalSettingControllers) { + const externalSettingController = this._externalSettingControllers[key]; + externalSettingController.beforeDestroy(); + } + for (const key in this._wrappedSettings) { + const setting = this._wrappedSettings[key]; + setting.beforeDestroy(); + } + } } diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts index 092e33f03..53ff81130 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts @@ -7,8 +7,7 @@ import type { SettingsKeysFromTuple } from "../../interfacesAndTypes/utils"; import type { MakeSettingTypesMap, Settings } from "../../settings/settingsDefinitions"; import type { SettingsContextDelegate } from "../SettingsContextDelegate"; -class DependencyLoadingError extends Error { -} +class DependencyLoadingError extends Error {} /* * Dependency class is used to represent a node in the dependency graph of a data provider settings context. @@ -70,6 +69,13 @@ export class Dependency< this.getHelperDependency = this.getHelperDependency.bind(this); } + beforeDestroy() { + this._abortController?.abort(); + this._abortController = null; + this._dependencies.clear(); + this._loadingDependencies.clear(); + } + hasChildDependencies(): boolean { return this._numChildDependencies > 0; } @@ -234,7 +240,6 @@ export class Dependency< } this.callUpdateFunc(); } - private async callUpdateFunc() { if (this._abortController) { @@ -256,7 +261,7 @@ export class Dependency< if (e instanceof DependencyLoadingError) { return; } - + if (!isCancelledError(e)) { this.applyNewValue(null); return; diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts index 569401659..0c126d957 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts @@ -12,7 +12,6 @@ import { import type { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { ColorPaletteType } from "@framework/WorkbenchSettings"; - import type { PublishSubscribe } from "../../../utils/PublishSubscribeDelegate"; import { PublishSubscribeDelegate } from "../../../utils/PublishSubscribeDelegate"; import { GroupDelegate, GroupDelegateTopic } from "../../delegates/GroupDelegate"; @@ -176,6 +175,7 @@ export class DataProviderManager implements ItemGroup, PublishSubscribe implements Item, SharedSett } beforeDestroy(): void { + this._sharedSettingsDelegate.beforeDestroy(); this._sharedSettingsDelegate.unsubscribeAll(); } } diff --git a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/entities.ts b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/entities.ts index 3509b5083..630c5b147 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/entities.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/entities.ts @@ -1,4 +1,3 @@ - import type { GroupDelegate } from "../delegates/GroupDelegate"; import type { ItemDelegate } from "../delegates/ItemDelegate"; import type { SharedSettingsDelegate } from "../delegates/SharedSettingsDelegate"; From ffbd84d81bff613cacc269655ae8b66ab4b45263 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 7 May 2025 14:01:55 +0200 Subject: [PATCH 81/97] Immutability --- .../userCreatedItems/IntersectionPolylines.ts | 3 +- .../StatisticalSurfaceProvider.ts | 4 +- .../makeRealizationPolygonsLayer.ts | 11 +- .../modules/3DViewer/settings/settings.tsx | 4 +- .../view/components/PolylineEditingPanel.tsx | 4 +- .../components/SubsurfaceViewerWrapper.tsx | 3 +- .../framework/DataProvider/DataProvider.ts | 18 +- .../DataProviderManager.ts | 2 +- .../framework/utils/immutabilityUtils.ts | 171 ++++++++++++++++++ .../customDataProviderImplementation.ts | 12 +- .../visualization/VisualizationAssembler.ts | 2 +- .../makeDrilledWellTrajectoriesLayer.ts | 6 +- .../visualization/utils/colors.ts | 1 - 13 files changed, 204 insertions(+), 37 deletions(-) create mode 100644 frontend/src/modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils.ts diff --git a/frontend/src/framework/userCreatedItems/IntersectionPolylines.ts b/frontend/src/framework/userCreatedItems/IntersectionPolylines.ts index ae48c5103..6f4cacbe0 100644 --- a/frontend/src/framework/userCreatedItems/IntersectionPolylines.ts +++ b/frontend/src/framework/userCreatedItems/IntersectionPolylines.ts @@ -1,4 +1,3 @@ - import { atom } from "jotai"; import { cloneDeep } from "lodash"; import { v4 } from "uuid"; @@ -55,7 +54,7 @@ export class IntersectionPolylines implements UserCreatedItemSet { this.notifySubscribers(IntersectionPolylinesEvent.CHANGE); } - getPolylines(): IntersectionPolyline[] { + getPolylines(): readonly IntersectionPolyline[] { return this._polylines; } diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts index 081f8f28c..996a3fdd7 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts @@ -1,5 +1,3 @@ -import { isEqual } from "lodash"; - import type { SurfaceDataPng_api } from "@api"; import { SurfaceStatisticFunction_api, @@ -21,7 +19,7 @@ import { SurfaceAddressBuilder } from "@modules/_shared/Surface"; import type { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataTransforms"; import { transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; - +import { isEqual } from "lodash"; const statisicalSurfaceSettings = [ Setting.ENSEMBLE, diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts index 26bbc2ab2..419289d07 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts @@ -1,16 +1,15 @@ -import { GeoJsonLayer } from "@deck.gl/layers"; -import type { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "geojson"; - import type { PolygonData_api } from "@api"; +import { GeoJsonLayer } from "@deck.gl/layers"; +import type { DeepReadonly } from "@modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils"; import type { TransformerArgs } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; - +import type { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "geojson"; import type { RealizationPolygonsData, RealizationPolygonsSettings, } from "../customDataProviderImplementations/RealizationPolygonsProvider"; -function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { +function zipCoords(xArr: readonly number[], yArr: readonly number[], zArr: readonly number[]): number[][] { const coords: number[][] = []; for (let i = 0; i < xArr.length; i++) { coords.push([xArr[i], yArr[i], -zArr[i]]); @@ -19,7 +18,7 @@ function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { return coords; } -function polygonsToGeojson(polygons: PolygonData_api): Feature { +function polygonsToGeojson(polygons: DeepReadonly): Feature { const data: Feature = { type: "Feature", geometry: { diff --git a/frontend/src/modules/3DViewer/settings/settings.tsx b/frontend/src/modules/3DViewer/settings/settings.tsx index 798ee02de..1b1f4ecfe 100644 --- a/frontend/src/modules/3DViewer/settings/settings.tsx +++ b/frontend/src/modules/3DViewer/settings/settings.tsx @@ -32,7 +32,6 @@ import { resolveClassNames } from "@lib/utils/resolveClassNames"; import { usePropagateApiErrorToStatusWriter } from "@modules/_shared/hooks/usePropagateApiErrorToStatusWriter"; import { isoIntervalStringToDateLabel, isoStringToDateLabel } from "@modules/_shared/utils/isoDatetimeStringFormatting"; - import type { Interfaces } from "../interfaces"; import type { GridCellIndexRanges } from "../typesAndEnums"; @@ -71,7 +70,6 @@ import { drilledWellboreHeadersQueryAtom, gridModelInfosQueryAtom } from "./atom import { GridCellIndexFilter } from "./components/gridCellIndexFilter"; import { WellboreSelector } from "./components/wellboreSelector"; - export function Settings(props: ModuleSettingsProps): JSX.Element { const ensembleSet = useEnsembleSet(props.workbenchSession); const statusWriter = useSettingsStatusWriter(props.settingsContext); @@ -515,7 +513,7 @@ function makeWellHeaderOptions(wellHeaders: WellboreHeader_api[]): SelectOption[ } function makeCustomIntersectionPolylineOptions( - polylines: IntersectionPolyline[], + polylines: readonly IntersectionPolyline[], selectedId: string | null, filter: string, actions: React.ReactNode, diff --git a/frontend/src/modules/3DViewer/view/components/PolylineEditingPanel.tsx b/frontend/src/modules/3DViewer/view/components/PolylineEditingPanel.tsx index 9466c49db..bec1fcd29 100644 --- a/frontend/src/modules/3DViewer/view/components/PolylineEditingPanel.tsx +++ b/frontend/src/modules/3DViewer/view/components/PolylineEditingPanel.tsx @@ -15,7 +15,7 @@ export type PolylineEditingPanelProps = { currentlyEditedPolylineName?: string; selectedPolylineIndex: number | null; hoveredPolylineIndex: number | null; - intersectionPolylines: IntersectionPolyline[]; + intersectionPolylines: readonly IntersectionPolyline[]; onPolylinePointSelectionChange: (index: number | null) => void; onPolylineEditingModusChange: (active: boolean) => void; onDeleteCurrentlySelectedPoint: () => void; @@ -204,7 +204,7 @@ function makeSelectOptionsFromPoints(points: number[][]): SelectOption[] { })); } -function makeUniquePolylineName(intersectionPolylines: IntersectionPolyline[]): string { +function makeUniquePolylineName(intersectionPolylines: readonly IntersectionPolyline[]): string { const names = intersectionPolylines.map((polyline) => polyline.name); let i = 1; while (names.includes(`Polyline ${i}`)) { diff --git a/frontend/src/modules/3DViewer/view/components/SubsurfaceViewerWrapper.tsx b/frontend/src/modules/3DViewer/view/components/SubsurfaceViewerWrapper.tsx index fc7e44d7e..0677dd10f 100644 --- a/frontend/src/modules/3DViewer/view/components/SubsurfaceViewerWrapper.tsx +++ b/frontend/src/modules/3DViewer/view/components/SubsurfaceViewerWrapper.tsx @@ -25,7 +25,6 @@ import { createContinuousColorScaleForMap } from "../utils/colorTables"; import { PolylineEditingPanel } from "./PolylineEditingPanel"; import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; - export type BoundingBox3D = { xmin: number; ymin: number; @@ -55,7 +54,7 @@ export type SubsurfaceViewerWrapperProps = { onIntersectionPolylineEditCancel?: () => void; onVerticalScaleChange?: (verticalScale: number) => void; intersectionPolyline?: IntersectionPolyline; - intersectionPolylines?: IntersectionPolyline[]; + intersectionPolylines?: readonly IntersectionPolyline[]; }; type IntersectionZValues = { diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts index e11100170..7299990dc 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts @@ -25,6 +25,7 @@ import type { NullableStoredData, StoredData } from "../../interfacesAndTypes/sh import type { SettingsKeysFromTuple } from "../../interfacesAndTypes/utils"; import type { MakeSettingTypesMap, Settings } from "../../settings/settingsDefinitions"; import { type DataProviderManager, DataProviderManagerTopic } from "../DataProviderManager/DataProviderManager"; +import { makeDeepImmutable, makeImmutable } from "../utils/immutabilityUtils"; import { makeSettings } from "../utils/makeSettings"; export enum DataProviderTopic { @@ -116,7 +117,7 @@ export class DataProvider< private _status: DataProviderStatus = DataProviderStatus.IDLE; private _data: TData | null = null; private _error: StatusMessage | string | null = null; - private _valueRange: [number, number] | null = null; + private _valueRange: readonly [number, number] | null = null; private _isSubordinated: boolean = false; private _prevSettings: TSettingTypes | null = null; private _prevStoredData: NullableStoredData | null = null; @@ -277,7 +278,7 @@ export class DataProvider< this._publishSubscribeDelegate.notifySubscribers(DataProviderTopic.SUBORDINATED); } - getValueRange(): [number, number] | null { + getValueRange(): readonly [number, number] | null { return this._valueRange; } @@ -324,12 +325,15 @@ export class DataProvider< makeAccessors(): DataProviderInformationAccessors { return { - getSetting: (settingName) => this._settingsContextDelegate.getSettings()[settingName].getValue(), + getSetting: (settingName) => + makeImmutable(this._settingsContextDelegate.getSettings()[settingName].getValue()), getAvailableSettingValues: (settingName) => - this._settingsContextDelegate.getSettings()[settingName].getAvailableValues(), - getGlobalSetting: (settingName) => this._dataProviderManager.getGlobalSetting(settingName), - getStoredData: (key: keyof TStoredData) => this._settingsContextDelegate.getStoredData(key), - getData: () => this._data, + makeDeepImmutable(this._settingsContextDelegate.getSettings()[settingName].getAvailableValues()), + getGlobalSetting: (settingName) => + makeDeepImmutable(this._dataProviderManager.getGlobalSetting(settingName)), + getStoredData: (key: keyof TStoredData) => + makeDeepImmutable(this._settingsContextDelegate.getStoredData(key)), + getData: () => makeDeepImmutable(this._data), getWorkbenchSession: () => this._dataProviderManager.getWorkbenchSession(), getWorkbenchSettings: () => this._dataProviderManager.getWorkbenchSettings(), }; diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts index 0c126d957..bc0082bcd 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts @@ -38,7 +38,7 @@ export type GlobalSettings = { fieldId: string | null; ensembles: readonly RegularEnsemble[]; realizationFilterFunction: EnsembleRealizationFilterFunction; - intersectionPolylines: IntersectionPolyline[]; + intersectionPolylines: readonly IntersectionPolyline[]; }; /* diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils.ts new file mode 100644 index 000000000..ff28d0e97 --- /dev/null +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils.ts @@ -0,0 +1,171 @@ +import { cloneDeep } from "lodash"; + +/** + * Checks whether a type is considered a plain object. + * + * Plain objects are: + * - Objects whose prototype is Object.prototype or null (not detectable in TypeScript) + * - Arrays, functions, Dates, Maps, Sets, and class instances are NOT considered plain objects. + * + * This type is best-effort: it excludes the well-known non-plain types. + * However, user-defined class instances will NOT be excluded reliably in the type system. + * Use ImmutableObject for mixed structures. + */ +type IsPlainObject = T extends object + ? T extends (...args: any[]) => any + ? false + : T extends abstract new (...args: any[]) => any + ? false + : T extends any[] + ? false + : T extends Date + ? false + : T extends Map + ? false + : T extends Set + ? false + : true + : false; + +/** + * DeepReadonly + * + * Recursively makes a plain object or array deeply readonly. + * Class instances and other non-plain objects are left untouched (no readonly modifiers added). + * + * Use this for pure data (POJOs, arrays). For mixed objects (POJOs + class instances), use ImmutableObject. + */ +export type DeepReadonly = T extends (...args: any[]) => any + ? T + : T extends abstract new (...args: any[]) => any + ? T + : T extends Array + ? ReadonlyArray> + : IsPlainObject extends true + ? { readonly [K in keyof T]: DeepReadonly } + : T; + +/** + * ImmutableObject + * + * Makes only the top-level properties readonly. + * The contained values (including class instances) are left untouched. + * + * Use this when the object can contain a mix of plain data and class instances. + */ +export type ImmutableObject = { + readonly [K in keyof T]: T[K]; +}; + +/** + * Deep freezes a value recursively. + * Skips class instances and only freezes plain objects, arrays, maps, and sets. + */ +function deepFreeze(obj: T): DeepReadonly { + return internalDeepFreeze(obj, new WeakSet()) as DeepReadonly; +} + +function isPlainObject(value: unknown): value is Record { + if (typeof value !== "object" || value === null) return false; + const proto = Object.getPrototypeOf(value); + return proto === Object.prototype || proto === null; +} + +function internalDeepFreeze(obj: unknown, seen: WeakSet): unknown { + if (obj === null || obj === undefined || typeof obj !== "object") { + return obj; + } + + if (seen.has(obj)) { + return obj; + } + seen.add(obj); + + if (Array.isArray(obj)) { + for (const item of obj) { + internalDeepFreeze(item, seen); + } + } else if (obj instanceof Map) { + for (const [key, value] of obj.entries()) { + internalDeepFreeze(key, seen); + internalDeepFreeze(value, seen); + } + } else if (obj instanceof Set) { + for (const value of obj.values()) { + internalDeepFreeze(value, seen); + } + } else if (isPlainObject(obj)) { + for (const key of Object.keys(obj)) { + internalDeepFreeze((obj as any)[key], seen); + } + } else { + // Class instance or other special object → do not freeze! + return obj; + } + + Object.freeze(obj); + return obj; +} + +/** + * Makes a value immutable (readonly) at runtime and type level. + * - For null/undefined/functions → returns as-is. + * - For plain objects and arrays → returns deeply frozen version. + * - For class instances → returns a frozen copy (no runtime immutability), but prevents modification via ImmutableObject if needed. + */ +export function makeDeepImmutable(value: T): DeepReadonly { + if (value === null || value === undefined) { + return value as DeepReadonly; + } + + if (typeof value === "function") { + return value as DeepReadonly; + } + + if (typeof value === "object") { + return deepFreeze(cloneDeep(value)) as DeepReadonly; + } + + return value as DeepReadonly; +} + +function isClassInstance(value: any): boolean { + return ( + typeof value === "object" && + value !== null && + Object.getPrototypeOf(value) !== Object.prototype && + Object.getPrototypeOf(value) !== null + ); +} + +/** + * Makes an object immutable at the top level (readonly at type level, frozen at runtime), + * but does not deep freeze nested values. + * + * Suitable for mixed objects (class instances + plain objects). + */ +export function makeImmutable(value: T): T { + if (value === null || value === undefined) { + return value; + } + + if (typeof value === "function") { + return value; + } + + if (typeof value === "object") { + if (isClassInstance(value)) { + // 🚨 Do not clone/freeze class instances → just return them + return value as T; + } + + if (Array.isArray(value)) { + return Object.freeze([...value]) as T; + } + + // ✅ Shallow freeze + return Object.freeze({ ...value }); + } + + return value; +} diff --git a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts index 8bbbc057c..07c76c9a9 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts @@ -4,13 +4,13 @@ import type { WorkbenchSession } from "@framework/WorkbenchSession"; import type { WorkbenchSettings } from "@framework/WorkbenchSettings"; import type { GlobalSettings } from "../framework/DataProviderManager/DataProviderManager"; +import type { DeepReadonly } from "../framework/utils/immutabilityUtils"; import type { MakeSettingTypesMap, Settings } from "../settings/settingsDefinitions"; import type { CustomSettingsHandler } from "./customSettingsHandler"; import type { NullableStoredData, StoredData } from "./sharedTypes"; import type { AvailableValuesType, SettingsKeysFromTuple } from "./utils"; - /** * This type is used to pass parameters to the fetchData method of a CustomDataProviderImplementation. * It contains accessors to the data and settings of the provider and other useful information. @@ -26,7 +26,7 @@ export type DataProviderInformationAccessors< * Access the data that the provider is currently storing. * @returns The data that the provider is currently storing, or null if the provider has no data. */ - getData: () => TData | null; + getData: () => DeepReadonly | null; /** * Access the settings of the provider. @@ -50,7 +50,7 @@ export type DataProviderInformationAccessors< * const availableValues = getAvailableSettingValues("settingName"); * ``` */ - getAvailableSettingValues: (settingName: K) => AvailableValuesType | null; + getAvailableSettingValues: (settingName: K) => DeepReadonly> | null; /** * Access the global settings of the data provider manager. @@ -63,7 +63,7 @@ export type DataProviderInformationAccessors< * const value = getGlobalSetting("settingName"); * ``` */ - getGlobalSetting: (settingName: T) => GlobalSettings[T]; + getGlobalSetting: (settingName: T) => DeepReadonly; /** * Access the stored data of the provider. @@ -76,7 +76,7 @@ export type DataProviderInformationAccessors< * const storedData = getStoredData("key"); * ``` */ - getStoredData: (key: K) => TStoredData[K] | null; + getStoredData: (key: K) => DeepReadonly | null; /** * Access to the workbench session. @@ -162,7 +162,7 @@ export interface CustomDataProviderImplementation< */ makeValueRange?( accessors: DataProviderInformationAccessors, - ): [number, number] | null; + ): readonly [number, number] | null; /** * This method is called to check if the current settings are valid. It should return true if the settings are valid diff --git a/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts b/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts index 67595fb36..d2b3a86aa 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts @@ -63,7 +63,7 @@ export type TransformerArgs< name: string; isLoading: boolean; getInjectedData: () => TInjectedData; - getValueRange: () => [number, number] | null; + getValueRange: () => Readonly<[number, number]> | null; }; export interface HoverVisualizationsFunction { diff --git a/frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts b/frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts index b1bdc72cf..e8fb5878d 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts @@ -4,11 +4,11 @@ import type { Feature, GeoJsonProperties, GeometryCollection, LineString, Point import type { WellboreTrajectory_api } from "@api"; import { AdvancedWellsLayer } from "@modules/_shared/customDeckGlLayers/AdvancedWellsLayer"; - +import type { DeepReadonly } from "../../framework/utils/immutabilityUtils"; import type { TransformerArgs } from "../VisualizationAssembler"; function wellTrajectoryToGeojson( - wellTrajectory: WellboreTrajectory_api, + wellTrajectory: DeepReadonly, ): Feature { const point: Point = { type: "Point", @@ -44,7 +44,7 @@ function wellTrajectoryToGeojson( return geometryCollection; } -function zipCoords(xArr: number[], yArr: number[], zArr: number[]): number[][] { +function zipCoords(xArr: readonly number[], yArr: readonly number[], zArr: readonly number[]): number[][] { const coords: number[][] = []; for (let i = 0; i < xArr.length; i++) { coords.push([xArr[i], yArr[i], -zArr[i]]); diff --git a/frontend/src/modules/_shared/DataProviderFramework/visualization/utils/colors.ts b/frontend/src/modules/_shared/DataProviderFramework/visualization/utils/colors.ts index 9b2a07c18..90e0e2bbb 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/visualization/utils/colors.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/visualization/utils/colors.ts @@ -1,4 +1,3 @@ - import type { Rgb } from "culori"; import { parse } from "culori"; From ec56a6f7c1238248041e72885f6e027ce1c765ff Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Wed, 7 May 2025 15:55:50 +0200 Subject: [PATCH 82/97] Added attribute dependencies to groups Other minor fixes --- .../modules/2DViewer/settings/settings.tsx | 3 - .../delegates/SettingsContextDelegate.ts | 26 +++- .../delegates/SharedSettingsDelegate.ts | 135 +++++++++++++++++- .../delegates/_utils/Dependency.ts | 19 ++- .../framework/Group/Group.ts | 12 +- .../SettingManager/SettingManager.ts | 7 +- .../customGroupImplementation.ts | 8 +- .../customSettingsHandler.ts | 18 ++- 8 files changed, 193 insertions(+), 35 deletions(-) diff --git a/frontend/src/modules/2DViewer/settings/settings.tsx b/frontend/src/modules/2DViewer/settings/settings.tsx index df5c4b31a..b5af7cbe4 100644 --- a/frontend/src/modules/2DViewer/settings/settings.tsx +++ b/frontend/src/modules/2DViewer/settings/settings.tsx @@ -9,7 +9,6 @@ import { useEnsembleSet } from "@framework/WorkbenchSession"; import { CollapsibleGroup } from "@lib/components/CollapsibleGroup"; import { GroupDelegateTopic } from "@modules/_shared/DataProviderFramework/delegates/GroupDelegate"; - import { DataProviderManager, DataProviderManagerTopic, @@ -19,7 +18,6 @@ import { dataProviderManagerAtom, preferredViewLayoutAtom, userSelectedFieldIden import { selectedFieldIdentifierAtom } from "./atoms/derivedAtoms"; import { DataProviderManagerWrapper } from "./components/dataProviderManagerWrapper"; - export function Settings(props: ModuleSettingsProps): React.ReactNode { const ensembleSet = useEnsembleSet(props.workbenchSession); const queryClient = useQueryClient(); @@ -112,7 +110,6 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode { .makeSubscriberFunction(GroupDelegateTopic.CHILDREN_EXPANSION_STATES)(persistState); return function onUnmountEffect() { - dataProviderManager.beforeDestroy(); unsubscribeDataRev(); unsubscribeExpands(); }; diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts index 991384369..01d904ca2 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts @@ -340,12 +340,21 @@ export class SettingsContextDelegate< return this._settings[settingKey].isLoading(); }; + const localSettingManagerGetter = (key: K): SettingManager => { + return this._settings[key]; + }; + + const globalSettingGetter = (key: K): GlobalSettings[K] => { + return this.getDataProviderManager.bind(this)().getGlobalSetting(key); + }; + const availableSettingsUpdater = ( settingKey: K, updateFunc: UpdateFunc, TSettings, TSettingTypes, TSettingKey>, ): Dependency, TSettings, TSettingTypes, TSettingKey> => { const dependency = new Dependency, TSettings, TSettingTypes, TSettingKey>( - this, + localSettingManagerGetter, + globalSettingGetter, updateFunc, makeLocalSettingGetter, loadingStateGetter, @@ -379,7 +388,8 @@ export class SettingsContextDelegate< updateFunc: UpdateFunc, TSettings, TSettingTypes, TSettingKey>, ): Dependency, TSettings, TSettingTypes, TSettingKey> => { const dependency = new Dependency, TSettings, TSettingTypes, TSettingKey>( - this, + localSettingManagerGetter, + globalSettingGetter, updateFunc, makeLocalSettingGetter, loadingStateGetter, @@ -408,7 +418,14 @@ export class SettingsContextDelegate< TSettings, TSettingTypes, TSettingKey - >(this, updateFunc, makeLocalSettingGetter, loadingStateGetter, makeGlobalSettingGetter); + >( + localSettingManagerGetter, + globalSettingGetter, + updateFunc, + makeLocalSettingGetter, + loadingStateGetter, + makeGlobalSettingGetter, + ); this._dependencies.push(dependency); dependency.subscribe((storedData: TStoredData[K] | null) => { @@ -439,7 +456,8 @@ export class SettingsContextDelegate< }) => T, ) => { const dependency = new Dependency( - this, + localSettingManagerGetter, + globalSettingGetter, update, makeLocalSettingGetter, loadingStateGetter, diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts index d84f07591..8d252d6da 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts @@ -1,13 +1,21 @@ +import { DataProviderManagerTopic, type GlobalSettings } from "../framework/DataProviderManager/DataProviderManager"; import { ExternalSettingController } from "../framework/ExternalSettingController/ExternalSettingController"; -import type { SettingManager } from "../framework/SettingManager/SettingManager"; +import { SettingTopic, type SettingManager } from "../framework/SettingManager/SettingManager"; +import type { + DefineBasicDependenciesArgs, + SettingAttributes, + UpdateFunc, +} from "../interfacesAndTypes/customSettingsHandler"; import type { Item } from "../interfacesAndTypes/entities"; import type { SettingsKeysFromTuple } from "../interfacesAndTypes/utils"; -import type { SettingTypes, Settings } from "../settings/settingsDefinitions"; +import type { MakeSettingTypesMap, SettingTypes, Settings } from "../settings/settingsDefinitions"; +import { Dependency } from "./_utils/Dependency"; import { UnsubscribeHandlerDelegate } from "./UnsubscribeHandlerDelegate"; export class SharedSettingsDelegate< TSettings extends Settings, + TSettingTypes extends MakeSettingTypesMap = MakeSettingTypesMap, TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, > { private _externalSettingControllers: { [K in TSettingKey]: ExternalSettingController } = {} as { @@ -17,9 +25,22 @@ export class SharedSettingsDelegate< [K in TSettingKey]: SettingManager; }; private _unsubscribeHandler: UnsubscribeHandlerDelegate = new UnsubscribeHandlerDelegate(); + private _dependencies: Dependency[] = []; + private _parentItem: Item; + private _customDependenciesDefinition: + | ((args: DefineBasicDependenciesArgs) => void) + | null = null; - constructor(parentItem: Item, wrappedSettings: { [K in TSettingKey]: SettingManager }) { + constructor( + parentItem: Item, + wrappedSettings: { [K in TSettingKey]: SettingManager }, + customDependenciesDefinition?: ( + args: DefineBasicDependenciesArgs, + ) => void, + ) { this._wrappedSettings = wrappedSettings; + this._parentItem = parentItem; + this._customDependenciesDefinition = customDependenciesDefinition ?? null; for (const key in wrappedSettings) { const setting = wrappedSettings[key]; @@ -31,6 +52,8 @@ export class SharedSettingsDelegate< if (!dataProviderManager) { throw new Error("SharedSettingDelegate must have a parent item with a data provider manager."); } + + this.createDependencies(); } getWrappedSettings(): { [K in TSettingKey]: SettingManager } { @@ -51,5 +74,111 @@ export class SharedSettingsDelegate< const setting = this._wrappedSettings[key]; setting.beforeDestroy(); } + for (const dependency of this._dependencies) { + dependency.beforeDestroy(); + } + this._dependencies = []; + } + + createDependencies(): void { + this._unsubscribeHandler.unsubscribe("dependencies"); + + this._dependencies = []; + + const makeLocalSettingGetter = (key: K, handler: (value: TSettingTypes[K]) => void) => { + const handleChange = (): void => { + const setting = this._wrappedSettings[key]; + handler(setting.getValue() as unknown as TSettingTypes[K]); + }; + this._unsubscribeHandler.registerUnsubscribeFunction( + "dependencies", + this._wrappedSettings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.VALUE)( + handleChange, + ), + ); + + this._unsubscribeHandler.registerUnsubscribeFunction( + "dependencies", + this._wrappedSettings[key] + .getPublishSubscribeDelegate() + .makeSubscriberFunction(SettingTopic.IS_LOADING)(() => { + if (!this._wrappedSettings[key].isLoading()) { + handleChange(); + } + }), + ); + + this._unsubscribeHandler.registerUnsubscribeFunction( + "dependencies", + this._wrappedSettings[key] + .getPublishSubscribeDelegate() + .makeSubscriberFunction(SettingTopic.IS_PERSISTED)(handleChange), + ); + + return handleChange; + }; + + const makeGlobalSettingGetter = ( + key: K, + handler: (value: GlobalSettings[K]) => void, + ) => { + const handleChange = (): void => { + handler(this._parentItem.getItemDelegate().getDataProviderManager().getGlobalSetting(key)); + }; + this._unsubscribeHandler.registerUnsubscribeFunction( + "dependencies", + this._parentItem + .getItemDelegate() + .getDataProviderManager() + .getPublishSubscribeDelegate() + .makeSubscriberFunction(DataProviderManagerTopic.GLOBAL_SETTINGS)(handleChange), + ); + + return handleChange.bind(this); + }; + + const loadingStateGetter = (settingKey: K): boolean => { + return this._wrappedSettings[settingKey].isLoading(); + }; + + const localSettingManagerGetter = (key: K): SettingManager => { + return this._wrappedSettings[key]; + }; + + const globalSettingGetter = (key: K): GlobalSettings[K] => { + return this._parentItem.getItemDelegate().getDataProviderManager().getGlobalSetting(key); + }; + + const settingAttributesUpdater = ( + settingKey: K, + updateFunc: UpdateFunc, TSettings, TSettingTypes, TSettingKey>, + ): Dependency, TSettings, TSettingTypes, TSettingKey> => { + const dependency = new Dependency, TSettings, TSettingTypes, TSettingKey>( + localSettingManagerGetter.bind(this), + globalSettingGetter.bind(this), + updateFunc, + makeLocalSettingGetter, + loadingStateGetter, + makeGlobalSettingGetter, + ); + this._dependencies.push(dependency); + + dependency.subscribe((attributes: Partial | null) => { + if (attributes === null) { + return; + } + this._wrappedSettings[settingKey].updateAttributes(attributes); + }); + + dependency.initialize(); + + return dependency; + }; + + if (this._customDependenciesDefinition) { + this._customDependenciesDefinition({ + settingAttributesUpdater: settingAttributesUpdater.bind(this), + }); + } } } diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts index 53ff81130..ca769ac98 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts @@ -1,11 +1,10 @@ import { isCancelledError } from "@tanstack/react-query"; import type { GlobalSettings } from "../../framework/DataProviderManager/DataProviderManager"; -import { SettingTopic } from "../../framework/SettingManager/SettingManager"; +import { SettingTopic, type SettingManager } from "../../framework/SettingManager/SettingManager"; import type { UpdateFunc } from "../../interfacesAndTypes/customSettingsHandler"; import type { SettingsKeysFromTuple } from "../../interfacesAndTypes/utils"; import type { MakeSettingTypesMap, Settings } from "../../settings/settingsDefinitions"; -import type { SettingsContextDelegate } from "../SettingsContextDelegate"; class DependencyLoadingError extends Error {} @@ -31,7 +30,8 @@ export class Dependency< private _loadingDependencies: Set<(loading: boolean, hasDependencies: boolean) => void> = new Set(); private _isLoading = false; - private _contextDelegate: SettingsContextDelegate; + private _localSettingManagerGetter: (key: K) => SettingManager; + private _globalSettingGetter: (key: K) => GlobalSettings[K]; private _makeLocalSettingGetter: (key: K, handler: (value: TSettingTypes[K]) => void) => void; private _localSettingLoadingStateGetter: (key: K) => boolean; @@ -49,7 +49,8 @@ export class Dependency< private _numChildDependencies = 0; constructor( - contextDelegate: SettingsContextDelegate, + localSettingManagerGetter: (key: K) => SettingManager, + globalSettingGetter: (key: K) => GlobalSettings[K], updateFunc: UpdateFunc, makeLocalSettingGetter: (key: K, handler: (value: TSettingTypes[K]) => void) => void, localSettingLoadingStateGetter: (key: K) => boolean, @@ -58,7 +59,8 @@ export class Dependency< handler: (value: GlobalSettings[K]) => void, ) => void, ) { - this._contextDelegate = contextDelegate; + this._localSettingManagerGetter = localSettingManagerGetter; + this._globalSettingGetter = globalSettingGetter; this._updateFunc = updateFunc; this._makeLocalSettingGetter = makeLocalSettingGetter; this._localSettingLoadingStateGetter = localSettingLoadingStateGetter; @@ -126,7 +128,7 @@ export class Dependency< return this._cachedSettingsMap.get(settingName as string); } - const setting = this._contextDelegate.getSettings()[settingName]; + const setting = this._localSettingManagerGetter(settingName); const value = setting.getValue(); this._cachedSettingsMap.set(settingName as string, value); @@ -175,10 +177,7 @@ export class Dependency< this.invalidate(); }); - this._cachedGlobalSettingsMap.set( - settingName as string, - this._contextDelegate.getDataProviderManager().getGlobalSetting(settingName), - ); + this._cachedGlobalSettingsMap.set(settingName as string, this._globalSettingGetter(settingName)); return this._cachedGlobalSettingsMap.get(settingName as string); } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/Group/Group.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/Group/Group.ts index 50b72894b..ecab9c9a3 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/Group/Group.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/Group/Group.ts @@ -9,6 +9,7 @@ import type { CustomGroupImplementationWithSettings, } from "../../interfacesAndTypes/customGroupImplementation"; import { includesSettings } from "../../interfacesAndTypes/customGroupImplementation"; +import type { DefineBasicDependenciesArgs } from "../../interfacesAndTypes/customSettingsHandler"; import type { ItemGroup } from "../../interfacesAndTypes/entities"; import type { SerializedGroup } from "../../interfacesAndTypes/serialization"; import { SerializedType } from "../../interfacesAndTypes/serialization"; @@ -57,7 +58,7 @@ export class Group< private _type: GroupType; private _icon: React.ReactNode | null = null; private _emptyContentMessage: string | null = null; - private _sharedSettingsDelegate: SharedSettingsDelegate | null = null; + private _sharedSettingsDelegate: SharedSettingsDelegate | null = null; constructor(params: GroupParams) { const { dataProviderManager, customGroupImplementation, type } = params; @@ -65,12 +66,15 @@ export class Group< this._groupDelegate.setColor(dataProviderManager.makeGroupColor()); this._itemDelegate = new ItemDelegate(customGroupImplementation.getDefaultName(), 1, dataProviderManager); if (includesSettings(customGroupImplementation)) { - this._sharedSettingsDelegate = new SharedSettingsDelegate( + this._sharedSettingsDelegate = new SharedSettingsDelegate( this, makeSettings( customGroupImplementation.settings as unknown as TSettings, - customGroupImplementation.getDefaultSettingsValues() as unknown as TSettingTypes, + customGroupImplementation.getDefaultSettingsValues?.() ?? {}, ), + customGroupImplementation.defineDependencies as unknown as + | ((args: DefineBasicDependenciesArgs) => void) + | undefined, ); } this._type = type; @@ -95,7 +99,7 @@ export class Group< return this._emptyContentMessage; } - getSharedSettingsDelegate(): SharedSettingsDelegate | null { + getSharedSettingsDelegate(): SharedSettingsDelegate | null { return this._sharedSettingsDelegate; } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts index 0aadc7194..57bd0de22 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts @@ -220,7 +220,10 @@ export class SettingManager< return; } - Object.assign(this._attributes, attributes); + this._attributes = { + ...this._attributes, + ...attributes, + }; this._publishSubscribeDelegate.notifySubscribers(SettingTopic.ATTRIBUTES); } @@ -305,8 +308,6 @@ export class SettingManager< if (this._externalController) { this._externalController.getSetting().setLoading(loading); - this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_LOADING); - return; } this._loading = loading; diff --git a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customGroupImplementation.ts b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customGroupImplementation.ts index 7b0354ef4..a6bdacdae 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customGroupImplementation.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customGroupImplementation.ts @@ -1,5 +1,7 @@ import type { MakeSettingTypesMap, Settings } from "../settings/settingsDefinitions"; +import type { DefineBasicDependenciesArgs } from "./customSettingsHandler"; + /** * This interface is describing what methods and members a custom group must implement. * A custom group can contain settings but it does not have to. @@ -29,9 +31,11 @@ export interface CustomGroupImplementationWithSettings< * A method that returns the default values of the settings. * @returns The default values of the settings. */ - getDefaultSettingsValues(): TSettingTypes; + getDefaultSettingsValues?(): Partial; + + defineDependencies?(args: DefineBasicDependenciesArgs): void; } export function includesSettings(obj: any): obj is CustomGroupImplementationWithSettings { - return obj.settings !== undefined && obj.getDefaultSettingsValues !== undefined; + return obj.settings !== undefined; } diff --git a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts index 55d062330..c2e28aa8a 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts @@ -10,7 +10,6 @@ import type { MakeSettingTypesMap, Settings } from "../settings/settingsDefiniti import type { NullableStoredData, StoredData } from "./sharedTypes"; import type { AvailableValuesType, SettingsKeysFromTuple } from "./utils"; - export interface GetHelperDependency< TSettings extends Settings, TSettingTypes extends MakeSettingTypesMap, @@ -38,13 +37,24 @@ export interface UpdateFunc< }): TReturnValue; } +export interface DefineBasicDependenciesArgs< + TSettings extends Settings, + TSettingTypes extends MakeSettingTypesMap = MakeSettingTypesMap, + TKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, +> { + settingAttributesUpdater: ( + settingKey: TSettingKey, + update: UpdateFunc, TSettings, TSettingTypes, TKey>, + ) => Dependency, TSettings, TSettingTypes, TKey>; +} + export interface DefineDependenciesArgs< TSettings extends Settings, TStoredData extends StoredData = Record, TSettingTypes extends MakeSettingTypesMap = MakeSettingTypesMap, TKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, TStoredDataKey extends keyof TStoredData = keyof TStoredData, -> { +> extends DefineBasicDependenciesArgs { availableSettingsUpdater: ( settingKey: TSettingKey, update: UpdateFunc, TSettings, TSettingTypes, TKey>, @@ -53,10 +63,6 @@ export interface DefineDependenciesArgs< key: K, update: UpdateFunc[TStoredDataKey], TSettings, TSettingTypes, TKey>, ) => Dependency[TStoredDataKey], TSettings, TSettingTypes, TKey>; - settingAttributesUpdater: ( - settingKey: TSettingKey, - update: UpdateFunc, TSettings, TSettingTypes, TKey>, - ) => Dependency, TSettings, TSettingTypes, TKey>; helperDependency: ( update: (args: { getLocalSetting: (settingName: T) => TSettingTypes[T]; From 38f4ba3edd78ea79ef9bdc30d44434ed1cebb232 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 8 May 2025 09:36:19 +0200 Subject: [PATCH 83/97] fix: import order --- .../StatisticalSurfaceProvider.ts | 3 ++- .../visualization/makeRealizationPolygonsLayer.ts | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts index 996a3fdd7..29d492d1c 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts @@ -1,3 +1,5 @@ +import { isEqual } from "lodash"; + import type { SurfaceDataPng_api } from "@api"; import { SurfaceStatisticFunction_api, @@ -19,7 +21,6 @@ import { SurfaceAddressBuilder } from "@modules/_shared/Surface"; import type { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataTransforms"; import { transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; -import { isEqual } from "lodash"; const statisicalSurfaceSettings = [ Setting.ENSEMBLE, diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts index 419289d07..6704fe678 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts @@ -1,8 +1,9 @@ -import type { PolygonData_api } from "@api"; import { GeoJsonLayer } from "@deck.gl/layers"; +import type { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "geojson"; + +import type { PolygonData_api } from "@api"; import type { DeepReadonly } from "@modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils"; import type { TransformerArgs } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; -import type { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "geojson"; import type { RealizationPolygonsData, From 69ee7b61f79fbbbf08b8ebe3271d5722ef431600 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 8 May 2025 12:42:35 +0200 Subject: [PATCH 84/97] wip --- .../ViewWrapper/viewWrapper.tsx | 26 +------------ .../RealizationGridProvider.ts | 12 ++---- .../framework/DataProvider/DataProvider.ts | 7 +++- .../DataProvider/DataProviderComponent.tsx | 7 ++-- .../ExternalSettingController.ts | 13 +++++-- .../framework/Group/GroupComponent.tsx | 7 ++-- .../SettingManager/SettingManager.ts | 38 ++++++++++++------- .../SettingManagerComponent.tsx | 2 +- .../SharedSetting/SharedSettingComponent.tsx | 8 +++- .../customDataProviderImplementation.ts | 17 +++++++-- .../settings/settingsDefinitions.ts | 5 +++ 11 files changed, 78 insertions(+), 64 deletions(-) diff --git a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx index a7d18d99b..13f7bfea8 100644 --- a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx +++ b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx @@ -14,7 +14,6 @@ import { ChannelReceiverNodesWrapper } from "./private-components/channelReceive import { Header } from "./private-components/header"; import { ViewContent } from "./private-components/viewContent"; - type ViewWrapperProps = { isActive: boolean; moduleInstance: ModuleInstance; @@ -150,30 +149,7 @@ export const ViewWrapper: React.FC = (props) => { )} - {props.changingLayout && ( -
-
-
-
-
- )} +
({ gridSurfaceData, @@ -149,7 +145,7 @@ export class RealizationGridProvider areCurrentSettingsValid({ getSetting, - }: DataProviderInformationAccessors): boolean { + }: AreSettingsValidArgs): boolean { return ( getSetting(Setting.ENSEMBLE) !== null && getSetting(Setting.REALIZATION) !== null && diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts index 7299990dc..3639f345d 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts @@ -122,6 +122,7 @@ export class DataProvider< private _prevSettings: TSettingTypes | null = null; private _prevStoredData: NullableStoredData | null = null; private _currentTransactionId: number = 0; + private _settingsErrorMessages: string[] = []; constructor(params: DataProviderParams) { const { @@ -171,7 +172,11 @@ export class DataProvider< return true; } - return this._customDataProviderImpl.areCurrentSettingsValid(this.makeAccessors()); + this._settingsErrorMessages = []; + const reportError = (message: string) => { + this._settingsErrorMessages.push(message); + }; + return this._customDataProviderImpl.areCurrentSettingsValid({ ...this.makeAccessors(), reportError }); } handleSettingsAndStoredDataChange(): void { diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProviderComponent.tsx b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProviderComponent.tsx index 7a8ddd28e..ce624982c 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProviderComponent.tsx +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProviderComponent.tsx @@ -8,11 +8,10 @@ import { DenseIconButton } from "@lib/components/DenseIconButton"; import { SortableListItem } from "@lib/components/SortableList"; import { resolveClassNames } from "@lib/utils/resolveClassNames"; - import { usePublishSubscribeTopicValue } from "../../../utils/PublishSubscribeDelegate"; import { ItemDelegateTopic } from "../../delegates/ItemDelegate"; import type { SettingManager } from "../SettingManager/SettingManager"; -import { SettingComponent } from "../SettingManager/SettingManagerComponent"; +import { SettingManagerComponent } from "../SettingManager/SettingManagerComponent"; import { EditName } from "../utilityComponents/EditName"; import { RemoveItemButton } from "../utilityComponents/RemoveItemButton"; import { VisibilityToggle } from "../utilityComponents/VisibilityToggle"; @@ -32,7 +31,9 @@ export function DataProviderComponent(props: DataProviderComponentProps): React. if (!manager) { return null; } - return ; + return ( + + ); } function makeSettings(settings: Record>): React.ReactNode[] { diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts index 6511a082e..2a854b2af 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts @@ -98,11 +98,11 @@ export class ExternalSettingController< this._availableValuesMap.clear(); } - setAvailableValues(availableValues: AvailableValuesType | null): void { + setAvailableValues(settingId: string, availableValues: AvailableValuesType | null): void { if (availableValues) { - this._availableValuesMap.set(this._setting.getId(), availableValues); + this._availableValuesMap.set(settingId, availableValues); } else { - this._availableValuesMap.delete(this._setting.getId()); + this._availableValuesMap.delete(settingId); } this.makeIntersectionOfAvailableValues(); @@ -118,7 +118,7 @@ export class ExternalSettingController< ); } - const { reducer, startingValue } = reducerDefinition; + const { reducer, startingValue, isValid } = reducerDefinition; let availableValues: MakeAvailableValuesTypeBasedOnCategory = startingValue as MakeAvailableValuesTypeBasedOnCategory; let index = 0; @@ -134,6 +134,11 @@ export class ExternalSettingController< ) as MakeAvailableValuesTypeBasedOnCategory; } + if (!isValid(availableValues as any)) { + this._setting.setAvailableValues(null); + return; + } + this._setting.setAvailableValues(availableValues); } } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/Group/GroupComponent.tsx b/frontend/src/modules/_shared/DataProviderFramework/framework/Group/GroupComponent.tsx index a90ca492f..ac294c1d2 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/Group/GroupComponent.tsx +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/Group/GroupComponent.tsx @@ -3,7 +3,6 @@ import React from "react"; import { ColorSelect } from "@lib/components/ColorSelect"; import { SortableListGroup } from "@lib/components/SortableList"; - import { usePublishSubscribeTopicValue } from "../../../utils/PublishSubscribeDelegate"; import type { ActionGroup } from "../../Actions"; import { Actions } from "../../Actions"; @@ -11,7 +10,7 @@ import { GroupDelegateTopic } from "../../delegates/GroupDelegate"; import { ItemDelegateTopic } from "../../delegates/ItemDelegate"; import type { Item, ItemGroup } from "../../interfacesAndTypes/entities"; import type { SettingManager } from "../SettingManager/SettingManager"; -import { SettingComponent } from "../SettingManager/SettingManagerComponent"; +import { SettingManagerComponent } from "../SettingManager/SettingManagerComponent"; import { EditName } from "../utilityComponents/EditName"; import { EmptyContent } from "../utilityComponents/EmptyContent"; import { ExpandCollapseAllButton } from "../utilityComponents/ExpandCollapseAllButton"; @@ -49,7 +48,9 @@ export function GroupComponent(props: GroupComponentProps): React.ReactNode { if (!manager) { return null; } - return ; + return ( + + ); } function makeSettings(settings: SettingManager[]): React.ReactNode[] { diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts index 57bd0de22..a13865c38 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts @@ -189,6 +189,7 @@ export class SettingManager< unregisterExternalSettingController(): void { this._externalController = null; this._unsubscribeHandler.unsubscribe("external-setting-controller"); + this.applyAvailableValues(); } beforeDestroy(): void { @@ -423,6 +424,9 @@ export class SettingManager< } getAvailableValues(): AvailableValuesType | null { + if (this._externalController) { + return this._externalController.getSetting().getAvailableValues(); + } return this._availableValues; } @@ -466,19 +470,7 @@ export class SettingManager< return false; } - setAvailableValues(availableValues: MakeAvailableValuesTypeBasedOnCategory | null): void { - if (this._externalController) { - this.initialize(); - this._externalController.setAvailableValues(availableValues); - } - - if (isEqual(this._availableValues, availableValues) && this._initialized) { - this.setLoading(false); - return; - } - - this._availableValues = availableValues; - + private applyAvailableValues() { let valueChanged = false; const valueFixedUp = !this.checkIfValueIsValid(this.getValue()) && this.maybeFixupValue(); const persistedValueReset = this.maybeResetPersistedValue(); @@ -489,9 +481,27 @@ export class SettingManager< this.setValueValid(this.checkIfValueIsValid(this.getValue())); this.initialize(); this.setLoading(false); - if (valueChanged || this._isValueValid !== prevIsValid) { + if (valueChanged || this._isValueValid !== prevIsValid || this._value === null) { this._publishSubscribeDelegate.notifySubscribers(SettingTopic.VALUE); } + } + + setAvailableValues(availableValues: MakeAvailableValuesTypeBasedOnCategory | null): void { + if (this._externalController) { + this.initialize(); + this._externalController.setAvailableValues(this.getId(), availableValues); + this._availableValues = availableValues; + return; + } + + if (isEqual(this._availableValues, availableValues) && this._initialized) { + this.setLoading(false); + return; + } + + this._availableValues = availableValues; + + this.applyAvailableValues(); this._publishSubscribeDelegate.notifySubscribers(SettingTopic.AVAILABLE_VALUES); } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx index 03694d506..f373edd73 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx @@ -23,7 +23,7 @@ export type SettingComponentProps< sharedSetting: boolean; }; -export function SettingComponent< +export function SettingManagerComponent< TSetting extends Setting, TValue extends SettingTypes[TSetting] = SettingTypes[TSetting], TCategory extends SettingCategories[TSetting] = SettingCategories[TSetting], diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SharedSetting/SharedSettingComponent.tsx b/frontend/src/modules/_shared/DataProviderFramework/framework/SharedSetting/SharedSettingComponent.tsx index f8817db1b..ea78a60db 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SharedSetting/SharedSettingComponent.tsx +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SharedSetting/SharedSettingComponent.tsx @@ -9,7 +9,7 @@ import { resolveClassNames } from "@lib/utils/resolveClassNames"; import { usePublishSubscribeTopicValue } from "../../../utils/PublishSubscribeDelegate"; import { ItemDelegateTopic } from "../../delegates/ItemDelegate"; -import { SettingComponent } from "../SettingManager/SettingManagerComponent"; +import { SettingManagerComponent } from "../SettingManager/SettingManagerComponent"; import type { SharedSetting } from "./SharedSetting"; @@ -57,7 +57,11 @@ export function SharedSettingComponent(props: SharedSettingComponentProps): Reac hidden: !isExpanded, })} > - +
); diff --git a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts index 07c76c9a9..10b7090ad 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts @@ -91,6 +91,16 @@ export type DataProviderInformationAccessors< getWorkbenchSettings: () => WorkbenchSettings; }; +export type AreSettingsValidArgs< + TSettings extends Settings, + TData, + TStoredData extends StoredData = Record, + TSettingKey extends SettingsKeysFromTuple = SettingsKeysFromTuple, + TSettingTypes extends MakeSettingTypesMap = MakeSettingTypesMap, +> = DataProviderInformationAccessors & { + reportError: (error: string) => void; +}; + /** * This type is used to pass parameters to the fetchData method of a CustomDataProviderImplementation. * It contains accessors to the data and settings of the provider and other useful information. @@ -169,8 +179,9 @@ export interface CustomDataProviderImplementation< * and false if they are not. * As long as the settings are not valid, the provider will not fetch data. * - * @param accessors Accessors to the data and settings of the provider. - * @returns + * @param args Accessors to the data and settings of the provider plus a function that can be used to report an error if + * some settings are not valid. It can be called multiple times if multiple settings are not valid. + * @returns true if the settings are valid, false otherwise. */ - areCurrentSettingsValid?: (accessors: DataProviderInformationAccessors) => boolean; + areCurrentSettingsValid?: (args: AreSettingsValidArgs) => boolean; } diff --git a/frontend/src/modules/_shared/DataProviderFramework/settings/settingsDefinitions.ts b/frontend/src/modules/_shared/DataProviderFramework/settings/settingsDefinitions.ts index 547903977..4ac6cb747 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/settings/settingsDefinitions.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/settings/settingsDefinitions.ts @@ -138,6 +138,7 @@ type SettingCategoryAvailableValuesIntersectionReducerMap = { [K in SettingCategory]?: { reducer: AvailableValuesIntersectionReducer; startingValue: AvailableValuesType>; + isValid: (availableValues: AvailableValuesType>) => boolean; }; }; @@ -289,6 +290,7 @@ export const settingCategoryAvailableValuesIntersectionReducerMap: SettingCatego return accumulator.filter((value) => currentAvailableValues.some((av) => isEqual(av, value))); }, startingValue: [], + isValid: (availableValues) => availableValues.length > 0, }, [SettingCategory.MULTI_SELECT]: { reducer: (accumulator, currentAvailableValues) => { @@ -298,6 +300,7 @@ export const settingCategoryAvailableValuesIntersectionReducerMap: SettingCatego return accumulator.filter((value) => currentAvailableValues.some((av) => isEqual(av, value))); }, startingValue: [], + isValid: (availableValues) => availableValues.length > 0, }, [SettingCategory.NUMBER]: { reducer: (accumulator, currentAvailableValues) => { @@ -307,6 +310,7 @@ export const settingCategoryAvailableValuesIntersectionReducerMap: SettingCatego return [Math.max(min, currentMin), Math.min(max, currentMax)]; }, startingValue: [-Number.MAX_VALUE, Number.MAX_VALUE], + isValid: (availableValues) => availableValues[0] < availableValues[1], }, [SettingCategory.RANGE]: { reducer: (accumulator, currentAvailableValues) => { @@ -316,6 +320,7 @@ export const settingCategoryAvailableValuesIntersectionReducerMap: SettingCatego return [Math.max(min, currentMin), Math.min(max, currentMax)]; }, startingValue: [-Number.MAX_VALUE, Number.MAX_VALUE], + isValid: (availableValues) => availableValues[0] < availableValues[1], }, }; From 6265dd9aa54b00c3aa980d23428ded194c0a332e Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 8 May 2025 15:18:50 +0200 Subject: [PATCH 85/97] wip --- .../modules/2DViewer/view/components/LayersWrapper.tsx | 8 +++----- .../ExternalSettingController.ts | 4 +--- .../visualization/VisualizationAssembler.ts | 5 +++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx index fadbe3dec..16b894678 100644 --- a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx @@ -36,10 +36,7 @@ import { makeDrilledWellborePicksBoundingBox } from "@modules/_shared/DataProvid import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/DataProviderFramework/visualization/deckgl/boundingBoxes/makeDrilledWellTrajectoriesBoundingBox"; import { makeDrilledWellborePicksLayer } from "@modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellborePicksLayer"; import { makeDrilledWellTrajectoriesLayer } from "@modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer"; -import type { - Annotation, - VisualizationTarget, -} from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; +import type { VisualizationTarget } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; import { VisualizationAssembler, VisualizationItemType, @@ -133,13 +130,14 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { const viewports: ViewportType[] = []; const deckGlLayers: Layer[] = []; const viewportAnnotations: React.ReactNode[] = []; - const globalAnnotations: Annotation[] = []; const globalLayerIds: string[] = ["placeholder"]; let numLoadingLayers = 0; const assemblerProduct = VISUALIZATION_ASSEMBLER.make(props.layerManager); + const globalAnnotations = assemblerProduct.annotations; + const numViews = assemblerProduct.children.filter( (item) => item.itemType === VisualizationItemType.GROUP && item.groupType === GroupType.VIEW, ).length; diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts index 2a854b2af..c744c4d07 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts @@ -113,9 +113,7 @@ export class ExternalSettingController< const reducerDefinition = settingCategoryAvailableValuesIntersectionReducerMap[category]; if (!reducerDefinition) { - throw new Error( - `No reducer definition found for category ${category}. Please check the settings definitions.`, - ); + return; } const { reducer, startingValue, isValid } = reducerDefinition; diff --git a/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts b/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts index d2b3a86aa..2df753173 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/visualization/VisualizationAssembler.ts @@ -302,15 +302,16 @@ export class VisualizationAssembler< accumulatedData = product.accumulatedData; aggregatedErrorMessages.push(...product.aggregatedErrorMessages); hoverVisualizationFunctions.push(product.makeHoverVisualizationsFunction); - annotations.push(...product.annotations); numLoadingDataProviders += product.numLoadingDataProviders; maybeApplyBoundingBox(product.combinedBoundingBox); if (child instanceof Group) { - const group = this.makeGroup(child, product.children, annotations); + const group = this.makeGroup(child, product.children, product.annotations); children.push(group); continue; + } else { + annotations.push(...product.annotations); } children.push(...product.children); From 18acaf979dac6e7772a45e3c4042744b0ae979d7 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 8 May 2025 15:46:42 +0200 Subject: [PATCH 86/97] wip --- .../delegates/SettingsContextDelegate.ts | 31 ++++++++++++++----- .../DataProviderManager.ts | 10 ++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts index 01d904ca2..7dfc83094 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts @@ -90,6 +90,23 @@ export class SettingsContextDelegate< this._customSettingsHandler = customSettingsHandler; this._dataProviderManager = dataProviderManager; + this._unsubscribeHandler.registerUnsubscribeFunction( + "dependencies", + this.getDataProviderManager() + .getPublishSubscribeDelegate() + .makeSubscriberFunction(DataProviderManagerTopic.DESERIALIZATION_DONE)(() => { + this.initialize(); + }), + ); + + this._settings = settings; + + if (!this.getDataProviderManager().isDeserializing()) { + this.initialize(); + } + } + + private initialize() { this._unsubscribeHandler.registerUnsubscribeFunction( "dependencies", this.getDataProviderManager() @@ -99,23 +116,23 @@ export class SettingsContextDelegate< }), ); - for (const key in settings) { + for (const key in this._settings) { this._unsubscribeHandler.registerUnsubscribeFunction( "settings", - settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.VALUE)(() => { + this._settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.VALUE)(() => { this.handleSettingChanged(); }), ); this._unsubscribeHandler.registerUnsubscribeFunction( "settings", - settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_LOADING)(() => { - this.handleSettingsLoadingStateChanged(); - }), + this._settings[key].getPublishSubscribeDelegate().makeSubscriberFunction(SettingTopic.IS_LOADING)( + () => { + this.handleSettingsLoadingStateChanged(); + }, + ), ); } - this._settings = settings; - this.createDependencies(); } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts index bc0082bcd..76d2db0f9 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts @@ -25,6 +25,7 @@ export enum DataProviderManagerTopic { SETTINGS_CHANGED = "SETTINGS_CHANGED", DATA_REVISION = "DATA_REVISION", GLOBAL_SETTINGS = "GLOBAL_SETTINGS", + DESERIALIZATION_DONE = "SERIALIZATION_DONE", } export type DataProviderManagerTopicPayload = { @@ -32,6 +33,7 @@ export type DataProviderManagerTopicPayload = { [DataProviderManagerTopic.SETTINGS_CHANGED]: void; [DataProviderManagerTopic.DATA_REVISION]: number; [DataProviderManagerTopic.GLOBAL_SETTINGS]: GlobalSettings; + [DataProviderManagerTopic.DESERIALIZATION_DONE]: void; }; export type GlobalSettings = { @@ -165,6 +167,9 @@ export class DataProviderManager implements ItemGroup, PublishSubscribe Date: Thu, 8 May 2025 16:05:10 +0200 Subject: [PATCH 87/97] wip --- .../visualization/makeRealizationGridLayer.ts | 2 +- .../delegates/SettingsContextDelegate.ts | 21 +++---------------- .../delegates/SharedSettingsDelegate.ts | 2 +- .../framework/DataProvider/DataProvider.ts | 4 ++++ .../DataProvider/DataProviderComponent.tsx | 21 +++++++++++++------ .../DataProviderManager.ts | 6 ------ .../ExternalSettingController.ts | 20 ++++++++---------- .../framework/Group/Group.ts | 2 +- .../SettingManager/SettingManager.ts | 14 ++++++------- .../customDataProviderImplementation.ts | 2 +- 10 files changed, 42 insertions(+), 52 deletions(-) diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationGridLayer.ts b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationGridLayer.ts index c8097c353..bf02d1979 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationGridLayer.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationGridLayer.ts @@ -33,7 +33,7 @@ export function makeRealizationGridLayer({ polysData: polysNumberArray, propertiesData: gridParameterData.polyPropsFloat32Arr, ZIncreasingDownwards: false, - gridLines: showGridLines, + gridLines: showGridLines ?? false, material: { ambient: 0.4, diffuse: 0.7, shininess: 8, specularColor: [25, 25, 25] }, pickable: true, colorMapName: "Physics", diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts index 7dfc83094..862ba048b 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts @@ -64,8 +64,8 @@ export class SettingsContextDelegate< TStoredDataKey >; private _dataProviderManager: DataProviderManager; - private _settings: { [K in TSettingKey]: SettingManager } = {} as { - [K in TSettingKey]: SettingManager; + private _settings: { [K in TSettingKey]: SettingManager } = {} as { + [K in TSettingKey]: SettingManager; }; private _publishSubscribeDelegate = new PublishSubscribeDelegate(); private _unsubscribeHandler: UnsubscribeHandlerDelegate = new UnsubscribeHandlerDelegate(); @@ -90,23 +90,8 @@ export class SettingsContextDelegate< this._customSettingsHandler = customSettingsHandler; this._dataProviderManager = dataProviderManager; - this._unsubscribeHandler.registerUnsubscribeFunction( - "dependencies", - this.getDataProviderManager() - .getPublishSubscribeDelegate() - .makeSubscriberFunction(DataProviderManagerTopic.DESERIALIZATION_DONE)(() => { - this.initialize(); - }), - ); - this._settings = settings; - if (!this.getDataProviderManager().isDeserializing()) { - this.initialize(); - } - } - - private initialize() { this._unsubscribeHandler.registerUnsubscribeFunction( "dependencies", this.getDataProviderManager() @@ -513,7 +498,7 @@ export class SettingsContextDelegate< for (const key in this._settings) { this._settings[key].beforeDestroy(); } - this._settings = {} as { [K in TSettingKey]: SettingManager }; + this._settings = {} as { [K in TSettingKey]: SettingManager }; } private setStatus(status: SettingsContextStatus) { diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts index 8d252d6da..96cca6d15 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/SharedSettingsDelegate.ts @@ -56,7 +56,7 @@ export class SharedSettingsDelegate< this.createDependencies(); } - getWrappedSettings(): { [K in TSettingKey]: SettingManager } { + getWrappedSettings(): { [K in TSettingKey]: SettingManager } { return this._wrappedSettings; } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts index 3639f345d..cb77358d8 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts @@ -179,6 +179,10 @@ export class DataProvider< return this._customDataProviderImpl.areCurrentSettingsValid({ ...this.makeAccessors(), reportError }); } + getSettingsErrorMessages(): string[] { + return this._settingsErrorMessages; + } + handleSettingsAndStoredDataChange(): void { if (this._settingsContextDelegate.getStatus() === SettingsContextStatus.LOADING) { this.setStatus(DataProviderStatus.LOADING); diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProviderComponent.tsx b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProviderComponent.tsx index ce624982c..577dada85 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProviderComponent.tsx +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProviderComponent.tsx @@ -124,13 +124,22 @@ function EndActions(props: EndActionProps): React.ReactNode { } } if (status === DataProviderStatus.INVALID_SETTINGS) { + let errorMessage = "Invalid settings"; + const invalidSettings = props.dataProvider.getSettingsContextDelegate().getInvalidSettings(); + + if (invalidSettings.length > 0) { + errorMessage += `: ${invalidSettings.join(", ")}`; + } + errorMessage += "."; + + const customReportedErrors = props.dataProvider.getSettingsErrorMessages(); + if (customReportedErrors.length > 0) { + errorMessage += `\n${customReportedErrors.join("\n")}`; + } + errorMessage += "\nPlease check the settings."; + return ( -
+
); diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts index 76d2db0f9..3ac86855d 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager.ts @@ -25,7 +25,6 @@ export enum DataProviderManagerTopic { SETTINGS_CHANGED = "SETTINGS_CHANGED", DATA_REVISION = "DATA_REVISION", GLOBAL_SETTINGS = "GLOBAL_SETTINGS", - DESERIALIZATION_DONE = "SERIALIZATION_DONE", } export type DataProviderManagerTopicPayload = { @@ -33,7 +32,6 @@ export type DataProviderManagerTopicPayload = { [DataProviderManagerTopic.SETTINGS_CHANGED]: void; [DataProviderManagerTopic.DATA_REVISION]: number; [DataProviderManagerTopic.GLOBAL_SETTINGS]: GlobalSettings; - [DataProviderManagerTopic.DESERIALIZATION_DONE]: void; }; export type GlobalSettings = { @@ -167,9 +165,6 @@ export class DataProviderManager implements ItemGroup, PublishSubscribe { private _parentItem: Item; @@ -117,23 +117,21 @@ export class ExternalSettingController< } const { reducer, startingValue, isValid } = reducerDefinition; - let availableValues: MakeAvailableValuesTypeBasedOnCategory = - startingValue as MakeAvailableValuesTypeBasedOnCategory; + let availableValues: AvailableValuesType = startingValue as AvailableValuesType; let index = 0; + let isInvalid = false; for (const value of this._availableValuesMap.values()) { if (value === null) { - continue; + isInvalid = true; + break; } - availableValues = reducer( - availableValues as any, - value as any, - index++, - ) as MakeAvailableValuesTypeBasedOnCategory; + availableValues = reducer(availableValues as any, value as any, index++) as AvailableValuesType; } - if (!isValid(availableValues as any)) { + if (!isValid(availableValues as any) || isInvalid) { this._setting.setAvailableValues(null); + this._setting.setValue(null as any); return; } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/Group/Group.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/Group/Group.ts index ecab9c9a3..e872e97c9 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/Group/Group.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/Group/Group.ts @@ -103,7 +103,7 @@ export class Group< return this._sharedSettingsDelegate; } - getWrappedSettings(): { [K in TSettingKey]: SettingManager } { + getWrappedSettings(): { [K in TSettingKey]: SettingManager } { if (!this._sharedSettingsDelegate) { throw new Error("Group does not have shared settings."); } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts index a13865c38..63042c6ed 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts @@ -66,7 +66,7 @@ export enum OverriddenValueProviderType { */ export class SettingManager< TSetting extends Setting, - TValue extends SettingTypes[TSetting] = SettingTypes[TSetting], + TValue extends SettingTypes[TSetting] | null = SettingTypes[TSetting] | null, TCategory extends SettingCategories[TSetting] = SettingCategories[TSetting], > implements PublishSubscribe> { @@ -78,7 +78,7 @@ export class SettingManager< private _value: TValue; private _isValueValid: boolean = false; private _publishSubscribeDelegate = new PublishSubscribeDelegate>(); - private _availableValues: MakeAvailableValuesTypeBasedOnCategory | null = null; + private _availableValues: AvailableValuesType | null = null; private _loading: boolean = false; private _initialized: boolean = false; private _currentValueFromPersistence: TValue | null = null; @@ -427,7 +427,7 @@ export class SettingManager< if (this._externalController) { return this._externalController.getSetting().getAvailableValues(); } - return this._availableValues; + return this._availableValues as AvailableValuesType | null; } maybeResetPersistedValue(): boolean { @@ -449,7 +449,7 @@ export class SettingManager< if (customIsValueValidFunction) { isPersistedValueValid = customIsValueValidFunction( this._currentValueFromPersistence, - this._availableValues, + this._availableValues as any, ); } else { isPersistedValueValid = settingCategoryIsValueValidMap[this._category]( @@ -486,7 +486,7 @@ export class SettingManager< } } - setAvailableValues(availableValues: MakeAvailableValuesTypeBasedOnCategory | null): void { + setAvailableValues(availableValues: AvailableValuesType | null): void { if (this._externalController) { this.initialize(); this._externalController.setAvailableValues(this.getId(), availableValues); @@ -524,7 +524,7 @@ export class SettingManager< let candidate: TValue; if (this._customSettingImplementation.fixupValue) { - candidate = this._customSettingImplementation.fixupValue(this._value, this._availableValues); + candidate = this._customSettingImplementation.fixupValue(this._value, this._availableValues as any); } else { candidate = settingCategoryFixupMap[this._category]( this._value as any, @@ -547,7 +547,7 @@ export class SettingManager< return false; } if (this._customSettingImplementation.isValueValid) { - return this._customSettingImplementation.isValueValid(value, this._availableValues); + return this._customSettingImplementation.isValueValid(value, this._availableValues as any); } else { return settingCategoryIsValueValidMap[this._category](value as any, this._availableValues as any); } diff --git a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts index 10b7090ad..a92bb6d71 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts @@ -38,7 +38,7 @@ export type DataProviderInformationAccessors< * const value = getSetting("settingName"); * ``` */ - getSetting: (settingName: K) => TSettingTypes[K]; + getSetting: (settingName: K) => TSettingTypes[K] | null; /** * Access the available values of a setting. From 87fbedf72ac0878e5575430d67267d08b9f2f3f3 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Thu, 8 May 2025 16:11:54 +0200 Subject: [PATCH 88/97] fix: committed by mistake --- .../ViewWrapper/viewWrapper.tsx | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx index 13f7bfea8..0a47a7baf 100644 --- a/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx +++ b/frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx @@ -149,7 +149,30 @@ export const ViewWrapper: React.FC = (props) => { )} - + {props.changingLayout && ( +
+
+
+
+
+ )}
Date: Thu, 8 May 2025 16:21:13 +0200 Subject: [PATCH 89/97] fix: color legend titles becoming invisible --- .../ColorLegendsContainer/colorLegendsContainer.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/modules/_shared/components/ColorLegendsContainer/colorLegendsContainer.tsx b/frontend/src/modules/_shared/components/ColorLegendsContainer/colorLegendsContainer.tsx index 9160a4c92..615dc4c9b 100644 --- a/frontend/src/modules/_shared/components/ColorLegendsContainer/colorLegendsContainer.tsx +++ b/frontend/src/modules/_shared/components/ColorLegendsContainer/colorLegendsContainer.tsx @@ -1,4 +1,4 @@ -import type React from "react"; +import React from "react"; import type { ColorScale } from "@lib/utils/ColorScale"; import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; @@ -143,6 +143,7 @@ type ColorLegendProps = { }; function ColorLegend(props: ColorLegendProps): React.ReactNode { + const clipPathId = React.useId(); const barHeight = props.totalHeight - STYLE_CONSTANTS.offset; const barStartPosition = props.left + STYLE_CONSTANTS.nameLabelWidth + STYLE_CONSTANTS.textGap; @@ -280,7 +281,7 @@ function ColorLegend(props: ColorLegendProps): React.ReactNode { return ( - + {props.colorScale.getName()} From a3f9b84840d8c5338b63a9797c05f3678ec16232 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 9 May 2025 12:11:16 +0200 Subject: [PATCH 90/97] Fixes related to static settings and rerendering Fixed bug in ColorScale selector --- .../ColorScaleSelector/colorScaleSelector.tsx | 45 +++++++++---------- .../framework/DataProvider/DataProvider.ts | 20 ++++++++- .../ExternalSettingController.ts | 5 +++ .../SettingManagerComponent.tsx | 2 +- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/frontend/src/framework/components/ColorScaleSelector/colorScaleSelector.tsx b/frontend/src/framework/components/ColorScaleSelector/colorScaleSelector.tsx index 7e02949c2..4b22754b4 100644 --- a/frontend/src/framework/components/ColorScaleSelector/colorScaleSelector.tsx +++ b/frontend/src/framework/components/ColorScaleSelector/colorScaleSelector.tsx @@ -22,7 +22,6 @@ import { convertRemToPixels } from "@lib/utils/screenUnitConversions"; import type { Vec2 } from "@lib/utils/vec2"; import { point2Distance } from "@lib/utils/vec2"; - export type ColorScaleSpecification = { colorScale: ColorScale; areBoundariesUserDefined: boolean; @@ -54,6 +53,7 @@ export function ColorScaleSelector(props: ColorScaleSelectorProps): React.ReactN setPrevColorScaleSpecification(props.colorScaleSpecification); if (props.colorScaleSpecification) { setColorScaleSpecification(props.colorScaleSpecification); + setTempColorScaleSpecification(props.colorScaleSpecification); } } @@ -138,12 +138,12 @@ function ColorScaleSelectorDialog(props: ColorScaleSelectorProps): React.ReactNo const [lastSelectedSequentialColorPalette, setLastSelectedSequentialColorPalette] = React.useState( props.colorScaleSpecification?.colorScale.getGradientType() === ColorScaleGradientType.Sequential ? props.colorScaleSpecification?.colorScale.getColorPalette() - : props.workbenchSettings.getColorPalettes()[ColorPaletteType.ContinuousSequential][0] ?? "" + : (props.workbenchSettings.getColorPalettes()[ColorPaletteType.ContinuousSequential][0] ?? ""), ); const [lastSelectedDivergingColorPalette, setLastSelectedDivergingColorPalette] = React.useState( props.colorScaleSpecification?.colorScale.getGradientType() === ColorScaleGradientType.Diverging ? props.colorScaleSpecification?.colorScale.getColorPalette() - : props.workbenchSettings.getColorPalettes()[ColorPaletteType.ContinuousDiverging][0] ?? "" + : (props.workbenchSettings.getColorPalettes()[ColorPaletteType.ContinuousDiverging][0] ?? ""), ); if (!isEqual(props.colorScaleSpecification, prevColorScaleSpecification)) { @@ -163,7 +163,7 @@ function ColorScaleSelectorDialog(props: ColorScaleSelectorProps): React.ReactNo colorScaleSpecification.colorScale.getMax(), colorScaleSpecification.colorScale.getNumSteps(), colorScaleSpecification.colorScale.getDivMidPoint(), - colorScaleSpecification.areBoundariesUserDefined + colorScaleSpecification.areBoundariesUserDefined, ); } @@ -181,7 +181,7 @@ function ColorScaleSelectorDialog(props: ColorScaleSelectorProps): React.ReactNo colorScaleSpecification.colorScale.getMax(), colorScaleSpecification.colorScale.getNumSteps(), colorScaleSpecification.colorScale.getDivMidPoint(), - colorScaleSpecification.areBoundariesUserDefined + colorScaleSpecification.areBoundariesUserDefined, ); } @@ -199,7 +199,7 @@ function ColorScaleSelectorDialog(props: ColorScaleSelectorProps): React.ReactNo colorScaleSpecification.colorScale.getMax(), colorScaleSpecification.colorScale.getNumSteps(), colorScaleSpecification.colorScale.getDivMidPoint(), - colorScaleSpecification.areBoundariesUserDefined + colorScaleSpecification.areBoundariesUserDefined, ); } @@ -212,7 +212,7 @@ function ColorScaleSelectorDialog(props: ColorScaleSelectorProps): React.ReactNo colorScaleSpecification.colorScale.getMax(), numSteps, colorScaleSpecification.colorScale.getDivMidPoint(), - colorScaleSpecification.areBoundariesUserDefined + colorScaleSpecification.areBoundariesUserDefined, ); } @@ -225,7 +225,7 @@ function ColorScaleSelectorDialog(props: ColorScaleSelectorProps): React.ReactNo max: number, numSteps: number, divMid: number, - areBoundariesUserDefined: boolean + areBoundariesUserDefined: boolean, ) { const colorScale = new ColorScale({ colorPalette, @@ -244,7 +244,7 @@ function ColorScaleSelectorDialog(props: ColorScaleSelectorProps): React.ReactNo onChange(colorScaleSpecification); } }, - [onChange] + [onChange], ); const handleMinMaxDivMidPointChange = React.useCallback( @@ -257,10 +257,10 @@ function ColorScaleSelectorDialog(props: ColorScaleSelectorProps): React.ReactNo max, colorScaleSpecification.colorScale.getNumSteps(), divMidPoint ?? colorScaleSpecification.colorScale.getDivMidPoint(), - colorScaleSpecification.areBoundariesUserDefined + colorScaleSpecification.areBoundariesUserDefined, ); }, - [colorScaleSpecification, makeAndPropagateColorScale] + [colorScaleSpecification, makeAndPropagateColorScale], ); const handleAreBoundariesUserDefinedChange = React.useCallback( @@ -273,10 +273,10 @@ function ColorScaleSelectorDialog(props: ColorScaleSelectorProps): React.ReactNo colorScaleSpecification.colorScale.getMax(), colorScaleSpecification.colorScale.getNumSteps(), colorScaleSpecification.colorScale.getDivMidPoint(), - areBoundariesUserDefined + areBoundariesUserDefined, ); }, - [colorScaleSpecification, makeAndPropagateColorScale] + [colorScaleSpecification, makeAndPropagateColorScale], ); return ( @@ -387,15 +387,14 @@ function ColorScaleSetter(props: ColorScaleSetterProps): React.ReactNode { const handleMinMaxDivMidPointChange = React.useCallback(function handleMinMaxDivMidPointChange( min: number, max: number, - divMidPoint?: number + divMidPoint?: number, ) { setMin(min); setMax(max); if (divMidPoint !== undefined) { setDivMidPoint(divMidPoint); } - }, - []); + }, []); return (
@@ -439,10 +438,10 @@ function MinMaxDivMidPointSetter(props: MinMaxDivMidPointSetterProps): React.Rea const [divMidPoint, setDivMidPoint] = React.useState(props.divMidPoint); const [prevDivMidPoint, setPrevDivMidPoint] = React.useState(props.divMidPoint); const [areBoundariesUserDefined, setAreBoundariesUserDefined] = React.useState( - props.areBoundariesUserDefined + props.areBoundariesUserDefined, ); const [prevAreBoundariesUserDefined, setPrevAreBoundariesUserDefined] = React.useState( - props.areBoundariesUserDefined + props.areBoundariesUserDefined, ); const [isDragging, setIsDragging] = React.useState(false); @@ -520,7 +519,7 @@ function MinMaxDivMidPointSetter(props: MinMaxDivMidPointSetterProps): React.Rea const newRelativeDivMidPoint = Math.min( Math.max((dx + convertRemToPixels(0.75) - containerRect.left) / containerRect.width, 0), - 1 + 1, ); newDivMidPoint = min + newRelativeDivMidPoint * (max - min); @@ -550,7 +549,7 @@ function MinMaxDivMidPointSetter(props: MinMaxDivMidPointSetterProps): React.Rea document.removeEventListener("pointerup", handlePointerUp); }; }, - [onChange, onChangePreview, min, max] + [onChange, onChangePreview, min, max], ); function handleMinChange(value: string) { @@ -609,7 +608,7 @@ function MinMaxDivMidPointSetter(props: MinMaxDivMidPointSetterProps): React.Rea "z-50": isDragging, hidden: props.gradientType === ColorScaleGradientType.Sequential || !areBoundariesUserDefined, - } + }, )} style={{ left: `${(Math.abs(divMidPoint - min) / Math.abs(max - min)) * 100}%` }} ref={divMidPointRef} @@ -677,7 +676,7 @@ const ColorScalePaletteSelector: React.FC = (pro const [open, setOpen] = React.useState(false); const [selectedColorPalette, setSelectedColorPalette] = React.useState(props.selectedColorPalette); const [prevSelectedColorPalette, setPrevSelectedColorPalette] = React.useState( - props.selectedColorPalette + props.selectedColorPalette, ); if (prevSelectedColorPalette.getId() !== props.selectedColorPalette.getId()) { @@ -769,7 +768,7 @@ const ColorScalePaletteSelector: React.FC = (pro > {renderColorPalettes()}
- + , )}
); diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts index cb77358d8..8b0e2af5a 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts @@ -32,6 +32,7 @@ export enum DataProviderTopic { STATUS = "STATUS", DATA = "DATA", SUBORDINATED = "SUBORDINATED", + REVISION_NUMBER = "REVISION_NUMBER", } export enum DataProviderStatus { @@ -46,6 +47,7 @@ export type DataProviderPayloads = { [DataProviderTopic.STATUS]: DataProviderStatus; [DataProviderTopic.DATA]: TData; [DataProviderTopic.SUBORDINATED]: boolean; + [DataProviderTopic.REVISION_NUMBER]: number; }; export function isDataProvider(obj: any): obj is DataProvider { @@ -123,6 +125,7 @@ export class DataProvider< private _prevStoredData: NullableStoredData | null = null; private _currentTransactionId: number = 0; private _settingsErrorMessages: string[] = []; + private _revisionNumber: number = 0; constructor(params: DataProviderParams) { const { @@ -220,6 +223,12 @@ export class DataProvider< } if (!refetchRequired) { + // If the settings have changed but no refetch is required, it might be that the settings changes + // still require a rerender of the data provider. + if (this._status === DataProviderStatus.SUCCESS) { + this.incrementRevisionNumber(); + return; + } this.setStatus(DataProviderStatus.SUCCESS); return; } @@ -306,6 +315,9 @@ export class DataProvider< if (topic === DataProviderTopic.SUBORDINATED) { return this._isSubordinated; } + if (topic === DataProviderTopic.REVISION_NUMBER) { + return this._revisionNumber; + } }; return snapshotGetter; @@ -431,13 +443,19 @@ export class DataProvider< this._unsubscribeHandler.unsubscribeAll(); } + private incrementRevisionNumber(): void { + this._revisionNumber += 1; + this._publishSubscribeDelegate.notifySubscribers(DataProviderTopic.REVISION_NUMBER); + this._dataProviderManager.publishTopic(DataProviderManagerTopic.DATA_REVISION); + } + private setStatus(status: DataProviderStatus): void { if (this._status === status) { return; } this._status = status; - this._dataProviderManager.publishTopic(DataProviderManagerTopic.DATA_REVISION); + this.incrementRevisionNumber(); this._publishSubscribeDelegate.notifySubscribers(DataProviderTopic.STATUS); } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts index 737c37d9c..9ddad2c42 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts @@ -112,6 +112,11 @@ export class ExternalSettingController< const category = this._setting.getCategory(); const reducerDefinition = settingCategoryAvailableValuesIntersectionReducerMap[category]; + if (this._setting.isStatic()) { + this._setting.maybeResetPersistedValue(); + return; + } + if (!reducerDefinition) { return; } diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx index f373edd73..79abd9dea 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManagerComponent.tsx @@ -58,7 +58,7 @@ export function SettingManagerComponent< return null; } - if (props.sharedSetting && isInitialized && availableValues === null) { + if (props.sharedSetting && isInitialized && availableValues === null && !props.setting.isStatic()) { return (
{props.setting.getLabel()}
From 506dfee60d59dcbef79aae3b4c9139499a8b01cf Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 9 May 2025 12:15:05 +0200 Subject: [PATCH 91/97] Removed immutability for now To be added in later PR --- .../makeRealizationPolygonsLayer.ts | 3 +- .../framework/DataProvider/DataProvider.ts | 14 +- .../framework/utils/immutabilityUtils.ts | 171 ------------------ .../customDataProviderImplementation.ts | 9 +- .../makeDrilledWellTrajectoriesLayer.ts | 3 +- 5 files changed, 11 insertions(+), 189 deletions(-) delete mode 100644 frontend/src/modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils.ts diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts index 6704fe678..7109fee24 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts @@ -2,7 +2,6 @@ import { GeoJsonLayer } from "@deck.gl/layers"; import type { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "geojson"; import type { PolygonData_api } from "@api"; -import type { DeepReadonly } from "@modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils"; import type { TransformerArgs } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; import type { @@ -19,7 +18,7 @@ function zipCoords(xArr: readonly number[], yArr: readonly number[], zArr: reado return coords; } -function polygonsToGeojson(polygons: DeepReadonly): Feature { +function polygonsToGeojson(polygons: PolygonData_api): Feature { const data: Feature = { type: "Feature", geometry: { diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts index 8b0e2af5a..977da28a3 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider.ts @@ -25,7 +25,6 @@ import type { NullableStoredData, StoredData } from "../../interfacesAndTypes/sh import type { SettingsKeysFromTuple } from "../../interfacesAndTypes/utils"; import type { MakeSettingTypesMap, Settings } from "../../settings/settingsDefinitions"; import { type DataProviderManager, DataProviderManagerTopic } from "../DataProviderManager/DataProviderManager"; -import { makeDeepImmutable, makeImmutable } from "../utils/immutabilityUtils"; import { makeSettings } from "../utils/makeSettings"; export enum DataProviderTopic { @@ -346,15 +345,12 @@ export class DataProvider< makeAccessors(): DataProviderInformationAccessors { return { - getSetting: (settingName) => - makeImmutable(this._settingsContextDelegate.getSettings()[settingName].getValue()), + getSetting: (settingName) => this._settingsContextDelegate.getSettings()[settingName].getValue(), getAvailableSettingValues: (settingName) => - makeDeepImmutable(this._settingsContextDelegate.getSettings()[settingName].getAvailableValues()), - getGlobalSetting: (settingName) => - makeDeepImmutable(this._dataProviderManager.getGlobalSetting(settingName)), - getStoredData: (key: keyof TStoredData) => - makeDeepImmutable(this._settingsContextDelegate.getStoredData(key)), - getData: () => makeDeepImmutable(this._data), + this._settingsContextDelegate.getSettings()[settingName].getAvailableValues(), + getGlobalSetting: (settingName) => this._dataProviderManager.getGlobalSetting(settingName), + getStoredData: (key: keyof TStoredData) => this._settingsContextDelegate.getStoredData(key), + getData: () => this._data, getWorkbenchSession: () => this._dataProviderManager.getWorkbenchSession(), getWorkbenchSettings: () => this._dataProviderManager.getWorkbenchSettings(), }; diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils.ts deleted file mode 100644 index ff28d0e97..000000000 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { cloneDeep } from "lodash"; - -/** - * Checks whether a type is considered a plain object. - * - * Plain objects are: - * - Objects whose prototype is Object.prototype or null (not detectable in TypeScript) - * - Arrays, functions, Dates, Maps, Sets, and class instances are NOT considered plain objects. - * - * This type is best-effort: it excludes the well-known non-plain types. - * However, user-defined class instances will NOT be excluded reliably in the type system. - * Use ImmutableObject for mixed structures. - */ -type IsPlainObject = T extends object - ? T extends (...args: any[]) => any - ? false - : T extends abstract new (...args: any[]) => any - ? false - : T extends any[] - ? false - : T extends Date - ? false - : T extends Map - ? false - : T extends Set - ? false - : true - : false; - -/** - * DeepReadonly - * - * Recursively makes a plain object or array deeply readonly. - * Class instances and other non-plain objects are left untouched (no readonly modifiers added). - * - * Use this for pure data (POJOs, arrays). For mixed objects (POJOs + class instances), use ImmutableObject. - */ -export type DeepReadonly = T extends (...args: any[]) => any - ? T - : T extends abstract new (...args: any[]) => any - ? T - : T extends Array - ? ReadonlyArray> - : IsPlainObject extends true - ? { readonly [K in keyof T]: DeepReadonly } - : T; - -/** - * ImmutableObject - * - * Makes only the top-level properties readonly. - * The contained values (including class instances) are left untouched. - * - * Use this when the object can contain a mix of plain data and class instances. - */ -export type ImmutableObject = { - readonly [K in keyof T]: T[K]; -}; - -/** - * Deep freezes a value recursively. - * Skips class instances and only freezes plain objects, arrays, maps, and sets. - */ -function deepFreeze(obj: T): DeepReadonly { - return internalDeepFreeze(obj, new WeakSet()) as DeepReadonly; -} - -function isPlainObject(value: unknown): value is Record { - if (typeof value !== "object" || value === null) return false; - const proto = Object.getPrototypeOf(value); - return proto === Object.prototype || proto === null; -} - -function internalDeepFreeze(obj: unknown, seen: WeakSet): unknown { - if (obj === null || obj === undefined || typeof obj !== "object") { - return obj; - } - - if (seen.has(obj)) { - return obj; - } - seen.add(obj); - - if (Array.isArray(obj)) { - for (const item of obj) { - internalDeepFreeze(item, seen); - } - } else if (obj instanceof Map) { - for (const [key, value] of obj.entries()) { - internalDeepFreeze(key, seen); - internalDeepFreeze(value, seen); - } - } else if (obj instanceof Set) { - for (const value of obj.values()) { - internalDeepFreeze(value, seen); - } - } else if (isPlainObject(obj)) { - for (const key of Object.keys(obj)) { - internalDeepFreeze((obj as any)[key], seen); - } - } else { - // Class instance or other special object → do not freeze! - return obj; - } - - Object.freeze(obj); - return obj; -} - -/** - * Makes a value immutable (readonly) at runtime and type level. - * - For null/undefined/functions → returns as-is. - * - For plain objects and arrays → returns deeply frozen version. - * - For class instances → returns a frozen copy (no runtime immutability), but prevents modification via ImmutableObject if needed. - */ -export function makeDeepImmutable(value: T): DeepReadonly { - if (value === null || value === undefined) { - return value as DeepReadonly; - } - - if (typeof value === "function") { - return value as DeepReadonly; - } - - if (typeof value === "object") { - return deepFreeze(cloneDeep(value)) as DeepReadonly; - } - - return value as DeepReadonly; -} - -function isClassInstance(value: any): boolean { - return ( - typeof value === "object" && - value !== null && - Object.getPrototypeOf(value) !== Object.prototype && - Object.getPrototypeOf(value) !== null - ); -} - -/** - * Makes an object immutable at the top level (readonly at type level, frozen at runtime), - * but does not deep freeze nested values. - * - * Suitable for mixed objects (class instances + plain objects). - */ -export function makeImmutable(value: T): T { - if (value === null || value === undefined) { - return value; - } - - if (typeof value === "function") { - return value; - } - - if (typeof value === "object") { - if (isClassInstance(value)) { - // 🚨 Do not clone/freeze class instances → just return them - return value as T; - } - - if (Array.isArray(value)) { - return Object.freeze([...value]) as T; - } - - // ✅ Shallow freeze - return Object.freeze({ ...value }); - } - - return value; -} diff --git a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts index a92bb6d71..a44c0f6c9 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation.ts @@ -4,7 +4,6 @@ import type { WorkbenchSession } from "@framework/WorkbenchSession"; import type { WorkbenchSettings } from "@framework/WorkbenchSettings"; import type { GlobalSettings } from "../framework/DataProviderManager/DataProviderManager"; -import type { DeepReadonly } from "../framework/utils/immutabilityUtils"; import type { MakeSettingTypesMap, Settings } from "../settings/settingsDefinitions"; import type { CustomSettingsHandler } from "./customSettingsHandler"; @@ -26,7 +25,7 @@ export type DataProviderInformationAccessors< * Access the data that the provider is currently storing. * @returns The data that the provider is currently storing, or null if the provider has no data. */ - getData: () => DeepReadonly | null; + getData: () => TData | null; /** * Access the settings of the provider. @@ -50,7 +49,7 @@ export type DataProviderInformationAccessors< * const availableValues = getAvailableSettingValues("settingName"); * ``` */ - getAvailableSettingValues: (settingName: K) => DeepReadonly> | null; + getAvailableSettingValues: (settingName: K) => AvailableValuesType | null; /** * Access the global settings of the data provider manager. @@ -63,7 +62,7 @@ export type DataProviderInformationAccessors< * const value = getGlobalSetting("settingName"); * ``` */ - getGlobalSetting: (settingName: T) => DeepReadonly; + getGlobalSetting: (settingName: T) => GlobalSettings[T]; /** * Access the stored data of the provider. @@ -76,7 +75,7 @@ export type DataProviderInformationAccessors< * const storedData = getStoredData("key"); * ``` */ - getStoredData: (key: K) => DeepReadonly | null; + getStoredData: (key: K) => TStoredData[K] | null; /** * Access to the workbench session. diff --git a/frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts b/frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts index e8fb5878d..23d8c12ac 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer.ts @@ -4,11 +4,10 @@ import type { Feature, GeoJsonProperties, GeometryCollection, LineString, Point import type { WellboreTrajectory_api } from "@api"; import { AdvancedWellsLayer } from "@modules/_shared/customDeckGlLayers/AdvancedWellsLayer"; -import type { DeepReadonly } from "../../framework/utils/immutabilityUtils"; import type { TransformerArgs } from "../VisualizationAssembler"; function wellTrajectoryToGeojson( - wellTrajectory: DeepReadonly, + wellTrajectory: WellboreTrajectory_api, ): Feature { const point: Point = { type: "Point", From 039b1bd6b04ab0d83cab39bc62d91f3dc319bc7e Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 9 May 2025 14:20:06 +0200 Subject: [PATCH 92/97] wip --- .../annotations/makeColorScaleAnnotation.ts | 4 - .../makeRealizationGridBoundingBox.ts | 4 - .../makeSurfaceLayerBoundingBox.ts | 4 - .../ObservedSurfaceProvider.ts | 3 +- .../RealizationGridProvider.ts | 152 ++++-------------- .../RealizationPolygonsProvider.ts | 1 - .../RealizationSurfaceProvider.ts | 25 +-- .../StatisticalSurfaceProvider.ts | 20 +-- .../visualization/makeObservedSurfaceLayer.ts | 4 - .../visualization/makeRealizationGridLayer.ts | 37 +---- .../makeRealizationPolygonsLayer.ts | 7 +- .../makeRealizationSurfaceLayer.ts | 4 - .../makeStatisticalSurfaceLayer.ts | 4 - 13 files changed, 38 insertions(+), 231 deletions(-) diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/annotations/makeColorScaleAnnotation.ts b/frontend/src/modules/2DViewer/DataProviderFramework/annotations/makeColorScaleAnnotation.ts index 579da231e..a9f32545a 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/annotations/makeColorScaleAnnotation.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/annotations/makeColorScaleAnnotation.ts @@ -9,12 +9,8 @@ export function makeColorScaleAnnotation({ getSetting, id, name, -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/annotations/makeColorScaleAnnotation.ts getValueRange, -}: FactoryFunctionArgs<[Setting.COLOR_SCALE], any>): Annotation[] { -======== }: TransformerArgs<[Setting.COLOR_SCALE], any>): Annotation[] { ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/annotations/makeColorScaleAnnotation.ts const colorScale = getSetting(Setting.COLOR_SCALE)?.colorScale; if (!colorScale) { diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/boundingBoxes/makeRealizationGridBoundingBox.ts b/frontend/src/modules/2DViewer/DataProviderFramework/boundingBoxes/makeRealizationGridBoundingBox.ts index f2015a273..450647838 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/boundingBoxes/makeRealizationGridBoundingBox.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/boundingBoxes/makeRealizationGridBoundingBox.ts @@ -1,11 +1,7 @@ import type { BBox } from "@lib/utils/bbox"; import type { TransformerArgs } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/boundingBoxes/makeRealizationGridBoundingBox.ts -import type { RealizationGridData } from "../../../layers/implementations/RealizationGridLayer"; -======== import type { RealizationGridData } from "../customDataProviderImplementations/RealizationGridProvider"; ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/boundingBoxes/makeRealizationGridBoundingBox.ts export function makeRealizationGridBoundingBox({ getData }: TransformerArgs): BBox | null { const data = getData(); diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/boundingBoxes/makeSurfaceLayerBoundingBox.ts b/frontend/src/modules/2DViewer/DataProviderFramework/boundingBoxes/makeSurfaceLayerBoundingBox.ts index 020718a64..3bbe88f22 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/boundingBoxes/makeSurfaceLayerBoundingBox.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/boundingBoxes/makeSurfaceLayerBoundingBox.ts @@ -1,11 +1,7 @@ import type { BBox } from "@lib/utils/bbox"; import type { TransformerArgs } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/boundingBoxes/makeSurfaceLayerBoundingBox.ts -import type { RealizationSurfaceData } from "../../../layers/implementations/RealizationSurfaceLayer"; -======== import type { RealizationSurfaceData } from "../customDataProviderImplementations/RealizationSurfaceProvider"; ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/boundingBoxes/makeSurfaceLayerBoundingBox.ts export function makeSurfaceLayerBoundingBox({ getData }: TransformerArgs): BBox | null { const data = getData(); diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/ObservedSurfaceProvider.ts b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/ObservedSurfaceProvider.ts index 91ec86689..093c271f5 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/ObservedSurfaceProvider.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/ObservedSurfaceProvider.ts @@ -16,7 +16,6 @@ import type { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataT import { transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; - const observedSurfaceSettings = [ Setting.ENSEMBLE, Setting.ATTRIBUTE, @@ -199,7 +198,7 @@ export class ObservedSurfaceProvider resample_to_def_str: null, }, }); - + registerQueryKey(surfaceDataOptions.queryKey); const promise = queryClient diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationGridProvider.ts b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationGridProvider.ts index f22149adc..63c531156 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationGridProvider.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationGridProvider.ts @@ -8,31 +8,17 @@ import type { CustomDataProviderImplementation, DataProviderInformationAccessors, FetchDataParams, -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/dataProviders/implementations/RealizationGridLayer.ts -} from "@modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation"; -import { - CancelUpdate, - type DefineDependenciesArgs, -} from "@modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler"; -import type { MakeSettingTypesMap } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; -import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; - -import { isEqual } from "lodash"; -======== } from "@modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation"; import type { DefineDependenciesArgs } from "@modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler"; import type { MakeSettingTypesMap } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; import { Setting } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationGridProvider.ts const realizationGridSettings = [ Setting.ENSEMBLE, Setting.REALIZATION, Setting.ATTRIBUTE, Setting.GRID_NAME, - Setting.GRID_LAYER_I_RANGE, - Setting.GRID_LAYER_J_RANGE, - Setting.GRID_LAYER_K_RANGE, + Setting.GRID_LAYER_K, Setting.TIME_OR_INTERVAL, Setting.SHOW_GRID_LINES, Setting.COLOR_SCALE, @@ -45,10 +31,6 @@ export type RealizationGridData = { gridParameterData: GridMappedProperty_trans; }; -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/dataProviders/implementations/RealizationGridLayer.ts -export class RealizationGridLayer - implements CustomDataLayerImplementation -======== type StoredData = { availableGridDimensions: { i: number; @@ -59,7 +41,6 @@ type StoredData = { export class RealizationGridProvider implements CustomDataProviderImplementation ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationGridProvider.ts { settings = realizationGridSettings; @@ -92,9 +73,10 @@ export class RealizationGridProvider fetchData({ getSetting, + getStoredData, registerQueryKey, queryClient, - }: FetchDataParams): Promise<{ + }: FetchDataParams): Promise<{ gridSurfaceData: GridSurface_trans; gridParameterData: GridMappedProperty_trans; }> { @@ -106,66 +88,6 @@ export class RealizationGridProvider if (timeOrInterval === "NO_TIME") { timeOrInterval = null; } -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/dataProviders/implementations/RealizationGridLayer.ts - const [iMin, iMax] = getSetting(Setting.GRID_LAYER_I_RANGE) ?? [0, 0]; - const [jMin, jMax] = getSetting(Setting.GRID_LAYER_J_RANGE) ?? [0, 0]; - const [kMin, kMax] = getSetting(Setting.GRID_LAYER_K_RANGE) ?? [0, 0]; - const queryKey = [ - "gridParameter", - ensembleIdent, - gridName, - attribute, - timeOrInterval, - realizationNum, - iMin, - iMax, - jMin, - jMax, - kMin, - kMax, - ]; - registerQueryKey(queryKey); - - const gridParameterPromise = queryClient - .fetchQuery({ - ...getGridParameterOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - grid_name: gridName ?? "", - parameter_name: attribute ?? "", - parameter_time_or_interval_str: timeOrInterval, - realization_num: realizationNum ?? 0, - i_min: iMin, - i_max: iMax, - j_min: jMin, - j_max: jMax, - k_min: kMin, - k_max: kMax, - }, - }), - }) - .then(transformGridMappedProperty); - - const gridSurfacePromise = queryClient - .fetchQuery({ - ...getGridSurfaceOptions({ - query: { - case_uuid: ensembleIdent?.getCaseUuid() ?? "", - ensemble_name: ensembleIdent?.getEnsembleName() ?? "", - grid_name: gridName ?? "", - realization_num: realizationNum ?? 0, - i_min: iMin, - i_max: iMax, - j_min: jMin, - j_max: jMax, - k_min: kMin, - k_max: kMax, - }, - }), - }) - .then(transformGridSurface); -======== const availableDimensions = getStoredData("availableGridDimensions"); const layerIndex = getSetting(Setting.GRID_LAYER_K); const iMin = 0; @@ -214,7 +136,6 @@ export class RealizationGridProvider const gridParameterPromise = queryClient.fetchQuery(gridParameterOptions).then(transformGridMappedProperty); const gridSurfacePromise = queryClient.fetchQuery(gridSurfaceOptions).then(transformGridSurface); ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationGridProvider.ts return Promise.all([gridSurfacePromise, gridParameterPromise]).then(([gridSurfaceData, gridParameterData]) => ({ gridSurfaceData, @@ -230,9 +151,7 @@ export class RealizationGridProvider getSetting(Setting.REALIZATION) !== null && getSetting(Setting.GRID_NAME) !== null && getSetting(Setting.ATTRIBUTE) !== null && - getSetting(Setting.GRID_LAYER_K_RANGE) !== null && - getSetting(Setting.GRID_LAYER_I_RANGE) !== null && - getSetting(Setting.GRID_LAYER_J_RANGE) !== null && + getSetting(Setting.GRID_LAYER_K) !== null && getSetting(Setting.TIME_OR_INTERVAL) !== null ); } @@ -240,8 +159,9 @@ export class RealizationGridProvider defineDependencies({ helperDependency, availableSettingsUpdater, + storedDataUpdater, queryClient, - }: DefineDependenciesArgs) { + }: DefineDependenciesArgs) { availableSettingsUpdater(Setting.ENSEMBLE, ({ getGlobalSetting }) => { const fieldIdentifier = getGlobalSetting("fieldId"); const ensembles = getGlobalSetting("ensembles"); @@ -315,46 +235,12 @@ export class RealizationGridProvider return availableGridAttributes; }); - availableSettingsUpdater(Setting.GRID_LAYER_I_RANGE, ({ getLocalSetting, getHelperDependency }) => { - const gridName = getLocalSetting(Setting.GRID_NAME); - const data = getHelperDependency(realizationGridDataDep); - - if (!gridName || !data) { - return CancelUpdate; - } - - const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; - const availableGridLayers: [number, number] = [0, 0]; - if (gridDimensions) { - availableGridLayers[1] = gridDimensions.i_count; - } - - return availableGridLayers; - }); - - availableSettingsUpdater(Setting.GRID_LAYER_J_RANGE, ({ getLocalSetting, getHelperDependency }) => { + availableSettingsUpdater(Setting.GRID_LAYER_K, ({ getLocalSetting, getHelperDependency }) => { const gridName = getLocalSetting(Setting.GRID_NAME); const data = getHelperDependency(realizationGridDataDep); if (!gridName || !data) { - return CancelUpdate; - } - - const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; - const availableGridLayers: [number, number] = [0, 0]; - if (gridDimensions) { - availableGridLayers[1] = gridDimensions.j_count; - } - - return availableGridLayers; - }); - - availableSettingsUpdater(Setting.GRID_LAYER_K_RANGE, ({ getLocalSetting, getHelperDependency }) => { - const gridName = getLocalSetting(Setting.GRID_NAME); - const data = getHelperDependency(realizationGridDataDep); - - if (!gridName || !data) { - return CancelUpdate; + return [0, 0]; } const gridDimensions = data.find((gridModel) => gridModel.grid_name === gridName)?.dimensions ?? null; @@ -372,7 +258,7 @@ export class RealizationGridProvider const data = getHelperDependency(realizationGridDataDep); if (!gridName || !gridAttribute || !data) { - return CancelUpdate; + return []; } const gridAttributeArr = @@ -390,5 +276,25 @@ export class RealizationGridProvider return availableTimeOrIntervals; }); + + storedDataUpdater("availableGridDimensions", ({ getHelperDependency }) => { + const data = getHelperDependency(realizationGridDataDep); + + if (!data) { + return { + i: 0, + j: 0, + k: 0, + }; + } + + const gridDimensions = data[0].dimensions; + + return { + i: gridDimensions.i_count, + j: gridDimensions.j_count, + k: gridDimensions.k_count, + }; + }); } } diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationPolygonsProvider.ts b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationPolygonsProvider.ts index 80ec813ea..81f27f05f 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationPolygonsProvider.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationPolygonsProvider.ts @@ -10,7 +10,6 @@ import type { DefineDependenciesArgs } from "@modules/_shared/DataProviderFramew import type { MakeSettingTypesMap } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; import { Setting } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; - const realizationPolygonsSettings = [ Setting.ENSEMBLE, Setting.REALIZATION, diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationSurfaceProvider.ts b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationSurfaceProvider.ts index 6c78aec90..af26f4ea3 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationSurfaceProvider.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationSurfaceProvider.ts @@ -1,10 +1,6 @@ -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/dataProviders/implementations/RealizationSurfaceLayer.ts -import type { SurfaceAttributeType_api, SurfaceDataPng_api } from "@api"; -======== import { isEqual } from "lodash"; import type { SurfaceDataPng_api } from "@api"; ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationSurfaceProvider.ts import { SurfaceTimeType_api, getRealizationSurfacesMetadataOptions, getSurfaceDataOptions } from "@api"; import type { CustomDataProviderImplementation, @@ -20,7 +16,6 @@ import type { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataT import { transformSurfaceData } from "@modules/_shared/Surface/queryDataTransforms"; import { encodeSurfAddrStr } from "@modules/_shared/Surface/surfaceAddress"; - const realizationSurfaceSettings = [ Setting.ENSEMBLE, Setting.REALIZATION, @@ -47,10 +42,8 @@ export class RealizationSurfaceProvider settings = realizationSurfaceSettings; private _dataFormat: SurfaceDataFormat; - private _attributeTypesFilter: SurfaceAttributeType_api[] = []; - constructor(attributeTypesFilter?: SurfaceAttributeType_api[], dataFormat?: SurfaceDataFormat) { - this._attributeTypesFilter = attributeTypesFilter ?? []; + constructor(dataFormat?: SurfaceDataFormat) { this._dataFormat = dataFormat ?? SurfaceDataFormat.PNG; } @@ -152,17 +145,7 @@ export class RealizationSurfaceProvider } const availableAttributes = [ - ...Array.from( - new Set( - data.surfaces - .filter( - (el) => - this._attributeTypesFilter.includes(el.attribute_type) || - this._attributeTypesFilter.length === 0, - ) - .map((surface) => surface.attribute_name), - ), - ), + ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), ]; return availableAttributes; @@ -263,9 +246,7 @@ export class RealizationSurfaceProvider registerQueryKey(surfaceDataOptions.queryKey); const promise = queryClient - .fetchQuery( - surfaceDataOptions, - ) + .fetchQuery(surfaceDataOptions) .then((data) => ({ format: this._dataFormat, surfaceData: transformSurfaceData(data) })); return promise as Promise; diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts index 2088bbfc5..29d492d1c 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts @@ -1,10 +1,6 @@ -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/dataProviders/implementations/StatisticalSurfaceLayer.ts -import type { SurfaceAttributeType_api, SurfaceDataPng_api } from "@api"; -======== import { isEqual } from "lodash"; import type { SurfaceDataPng_api } from "@api"; ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts import { SurfaceStatisticFunction_api, SurfaceTimeType_api, @@ -53,10 +49,8 @@ export class StatisticalSurfaceProvider settings = statisicalSurfaceSettings; private _dataFormat: SurfaceDataFormat; - private _attributeTypesFilter: SurfaceAttributeType_api[] = []; - constructor(attributeTypesFilter?: SurfaceAttributeType_api[], dataFormat?: SurfaceDataFormat) { - this._attributeTypesFilter = attributeTypesFilter ?? []; + constructor(dataFormat?: SurfaceDataFormat) { this._dataFormat = dataFormat ?? SurfaceDataFormat.PNG; } @@ -153,17 +147,7 @@ export class StatisticalSurfaceProvider } const availableAttributes = [ - ...Array.from( - new Set( - data.surfaces - .filter( - (el) => - this._attributeTypesFilter.includes(el.attribute_type) || - this._attributeTypesFilter.length === 0, - ) - .map((surface) => surface.attribute_name), - ), - ), + ...Array.from(new Set(data.surfaces.map((surface) => surface.attribute_name))), ]; return availableAttributes; diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeObservedSurfaceLayer.ts b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeObservedSurfaceLayer.ts index af914c180..97e782e05 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeObservedSurfaceLayer.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeObservedSurfaceLayer.ts @@ -12,11 +12,7 @@ import { type ObservedSurfaceData, type ObservedSurfaceSettings, SurfaceDataFormat, -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeObservedSurfaceLayer.ts -} from "../../layers/implementations/ObservedSurfaceLayer"; -======== } from "../customDataProviderImplementations/ObservedSurfaceProvider"; ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeObservedSurfaceLayer.ts function calcBoundsForRotationAroundUpperLeftCorner(surfDef: SurfaceDef_api): [number, number, number, number] { const width = (surfDef.npoints_x - 1) * surfDef.inc_x; diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationGridLayer.ts b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationGridLayer.ts index 2e6254200..bf02d1979 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationGridLayer.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationGridLayer.ts @@ -1,21 +1,3 @@ -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeRealizationGridLayer.ts -import { ColorPalette } from "@lib/utils/ColorPalette"; -import { ColorScale, ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale"; -import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; -import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; -import { PreviewLayer } from "@modules/_shared/customDeckGlLayers/PreviewLayer/PreviewLayer"; -import { Grid3DLayer } from "@webviz/subsurface-viewer/dist/layers"; - -import { makeRealizationGridBoundingBox } from "./boundingBoxes/makeRealizationGridBoundingBox"; - -import type { RealizationGridData, RealizationGridSettings } from "../../layers/implementations/RealizationGridLayer"; - -export function makeRealizationGridLayer( - args: FactoryFunctionArgs, -): Grid3DLayer | PreviewLayer | null { - const { id, getData, getSetting, isLoading } = args; -======== import { Grid3DLayer } from "@webviz/subsurface-viewer/dist/layers"; import { Setting } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; @@ -32,28 +14,13 @@ export function makeRealizationGridLayer({ getData, getSetting, }: TransformerArgs): Grid3DLayer | null { ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationGridLayer.ts const data = getData(); - let colorScale = getSetting(Setting.COLOR_SCALE)?.colorScale; - const boundingBox = makeRealizationGridBoundingBox(args); + const colorScale = getSetting(Setting.COLOR_SCALE)?.colorScale; - if (!data || !boundingBox || !colorScale) { + if (!data) { return null; } - if (isLoading) { - colorScale = new ColorScale({ - colorPalette: new ColorPalette({ - name: "ResInsight", - colors: ["#EEEEEE", "#EFEFEF"], - id: "black-white", - }), - gradientType: ColorScaleGradientType.Sequential, - type: ColorScaleType.Continuous, - steps: 100, - }); - } - const { gridSurfaceData, gridParameterData } = data; const showGridLines = getSetting(Setting.SHOW_GRID_LINES); diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts index d80b43977..7109fee24 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts @@ -2,17 +2,12 @@ import { GeoJsonLayer } from "@deck.gl/layers"; import type { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "geojson"; import type { PolygonData_api } from "@api"; -import type { DeepReadonly } from "@modules/_shared/DataProviderFramework/framework/utils/immutabilityUtils"; import type { TransformerArgs } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; import type { RealizationPolygonsData, RealizationPolygonsSettings, -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeRealizationPolygonsLayer.ts -} from "../../layers/implementations/RealizationPolygonsLayer"; -======== } from "../customDataProviderImplementations/RealizationPolygonsProvider"; ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer.ts function zipCoords(xArr: readonly number[], yArr: readonly number[], zArr: readonly number[]): number[][] { const coords: number[][] = []; @@ -23,7 +18,7 @@ function zipCoords(xArr: readonly number[], yArr: readonly number[], zArr: reado return coords; } -function polygonsToGeojson(polygons: DeepReadonly): Feature { +function polygonsToGeojson(polygons: PolygonData_api): Feature { const data: Feature = { type: "Feature", geometry: { diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationSurfaceLayer.ts b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationSurfaceLayer.ts index f7237c70a..564c419c4 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationSurfaceLayer.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationSurfaceLayer.ts @@ -12,11 +12,7 @@ import { type RealizationSurfaceData, type RealizationSurfaceSettings, SurfaceDataFormat, -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeRealizationSurfaceLayer.ts -} from "../../layers/implementations/RealizationSurfaceLayer"; -======== } from "../customDataProviderImplementations/RealizationSurfaceProvider"; ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeRealizationSurfaceLayer.ts function calcBoundsForRotationAroundUpperLeftCorner(surfDef: SurfaceDef_api): [number, number, number, number] { const width = (surfDef.npoints_x - 1) * surfDef.inc_x; diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeStatisticalSurfaceLayer.ts b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeStatisticalSurfaceLayer.ts index c4f78e8d3..7c145b8e7 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeStatisticalSurfaceLayer.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeStatisticalSurfaceLayer.ts @@ -12,11 +12,7 @@ import { type StatisticalSurfaceData, type StatisticalSurfaceSettings, SurfaceDataFormat, -<<<<<<<< HEAD:frontend/src/modules/_shared/DataProviderFramework/visualization/deckgl/makeStatisticalSurfaceLayer.ts -} from "../../layers/implementations/StatisticalSurfaceLayer"; -======== } from "../customDataProviderImplementations/StatisticalSurfaceProvider"; ->>>>>>>> origin/dpf-improve-dep-tree:frontend/src/modules/2DViewer/DataProviderFramework/visualization/makeStatisticalSurfaceLayer.ts function calcBoundsForRotationAroundUpperLeftCorner(surfDef: SurfaceDef_api): [number, number, number, number] { const width = (surfDef.npoints_x - 1) * surfDef.inc_x; From 88ce502af724519b78256023723dc6e2ff47c240 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 9 May 2025 16:45:41 +0200 Subject: [PATCH 93/97] wip --- .../StatisticalSurfaceProvider.ts | 5 +- .../view/components/LayersWrapper.tsx | 2 - .../RealizationSeismicCrosslineProvider.ts} | 16 +- .../RealizationSeismicDepthProvider.ts} | 16 +- .../RealizationSeismicInlineProvider.ts} | 16 +- .../makeDrilledWellTrajectoriesLayer.ts | 6 +- .../makeIntersectionGrid3dLayer.ts | 12 +- .../makeRealizationSurfaceLayer.ts | 69 ++++ .../makeSeismicFenceMeshLayer.ts | 16 +- .../makeRealizationSurfaceLayer.ts | 65 ---- .../src/modules/3DViewerNew/interfaces.ts | 13 +- .../3DViewerNew/settings/atoms/baseAtoms.ts | 4 +- .../layerManagerComponentWrapper.tsx | 106 +++--- .../modules/3DViewerNew/settings/settings.tsx | 23 +- .../view/components/InteractionWrapper.tsx | 26 +- .../view/components/LayersWrapper.tsx | 331 ++++++++++-------- .../view/components/ReadoutWrapper.tsx | 4 +- .../view/utils/PolylinesPlugin.tsx | 12 +- .../src/modules/3DViewerNew/view/view.tsx | 2 + .../DrilledWellTrajectoriesProvider.ts | 7 +- .../DrilledWellborePicksProvider.ts | 17 +- .../delegates/SettingsContextDelegate.ts | 3 - .../delegates/_utils/Dependency.ts | 4 - .../customSettingsHandler.ts | 2 +- .../DrilledWellboresSetting.tsx | 2 +- 25 files changed, 410 insertions(+), 369 deletions(-) rename frontend/src/modules/3DViewerNew/{LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer.ts => DataProviderFramework/customDataProviderImplementations/RealizationSeismicCrosslineProvider.ts} (92%) rename frontend/src/modules/3DViewerNew/{LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts => DataProviderFramework/customDataProviderImplementations/RealizationSeismicDepthProvider.ts} (92%) rename frontend/src/modules/3DViewerNew/{LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer.ts => DataProviderFramework/customDataProviderImplementations/RealizationSeismicInlineProvider.ts} (92%) rename frontend/src/modules/3DViewerNew/{LayerFramework => DataProviderFramework}/visualization/makeDrilledWellTrajectoriesLayer.ts (83%) rename frontend/src/modules/3DViewerNew/{LayerFramework => DataProviderFramework}/visualization/makeIntersectionGrid3dLayer.ts (88%) create mode 100644 frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeRealizationSurfaceLayer.ts rename frontend/src/modules/3DViewerNew/{LayerFramework => DataProviderFramework}/visualization/makeSeismicFenceMeshLayer.ts (93%) delete mode 100644 frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts diff --git a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts index 29d492d1c..6d62f5ef9 100644 --- a/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts +++ b/frontend/src/modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider.ts @@ -64,7 +64,10 @@ export class StatisticalSurfaceProvider return "Statistical Surface"; } - doSettingsChangesRequireDataRefetch(prevSettings: SettingsWithTypes, newSettings: SettingsWithTypes): boolean { + doSettingsChangesRequireDataRefetch( + prevSettings: SettingsWithTypes | null, + newSettings: SettingsWithTypes, + ): boolean { return !isEqual(prevSettings, newSettings); } diff --git a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx index 4b34b492f..16b894678 100644 --- a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx @@ -118,8 +118,6 @@ VISUALIZATION_ASSEMBLER.registerDataProviderTransformers( }, ); -VISUALIZATION_FACTORY.registerViewFunction(GroupType.VIEW, View, () => ({})); - export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { const [prevBoundingBox, setPrevBoundingBox] = React.useState(null); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer.ts b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicCrosslineProvider.ts similarity index 92% rename from frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer.ts rename to frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicCrosslineProvider.ts index 9a1ff9343..dfaf1ef2b 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer.ts +++ b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicCrosslineProvider.ts @@ -4,12 +4,12 @@ import { transformSeismicSlice, } from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; import type { - CustomDataLayerImplementation, - DataLayerInformationAccessors, + CustomDataProviderImplementation, + DataProviderInformationAccessors, FetchDataParams, -} from "@modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation"; -import type { DefineDependenciesArgs } from "@modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler"; -import { type MakeSettingTypesMap, Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; +} from "@modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation"; +import type { DefineDependenciesArgs } from "@modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler"; +import { Setting, type MakeSettingTypesMap } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; import { isEqual } from "lodash"; @@ -30,9 +30,9 @@ export type RealizationSeismicCrosslineStoredData = { seismicCubeMeta: SeismicCubeMeta_api[]; }; -export class RealizationSeismicCrosslineLayer +export class RealizationSeismicCrosslineProvider implements - CustomDataLayerImplementation< + CustomDataProviderImplementation< RealizationSeismicCrosslineSettings, RealizationSeismicCrosslineData, RealizationSeismicCrosslineStoredData @@ -49,7 +49,7 @@ export class RealizationSeismicCrosslineLayer } makeValueRange( - accessors: DataLayerInformationAccessors< + accessors: DataProviderInformationAccessors< RealizationSeismicCrosslineSettings, RealizationSeismicCrosslineData, RealizationSeismicCrosslineStoredData diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicDepthProvider.ts similarity index 92% rename from frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts rename to frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicDepthProvider.ts index 9e4a6898b..4c3685e31 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer.ts +++ b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicDepthProvider.ts @@ -4,12 +4,12 @@ import { transformSeismicSlice, } from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; import type { - CustomDataLayerImplementation, - DataLayerInformationAccessors, + CustomDataProviderImplementation, + DataProviderInformationAccessors, FetchDataParams, -} from "@modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation"; -import type { DefineDependenciesArgs } from "@modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler"; -import { type MakeSettingTypesMap, Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; +} from "@modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation"; +import type { DefineDependenciesArgs } from "@modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler"; +import { Setting, type MakeSettingTypesMap } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; import { isEqual } from "lodash"; @@ -30,9 +30,9 @@ export type RealizationSeismicDepthSliceStoredData = { seismicCubeMeta: SeismicCubeMeta_api[]; }; -export class RealizationSeismicDepthSliceLayer +export class RealizationSeismicDepthSliceProvider implements - CustomDataLayerImplementation< + CustomDataProviderImplementation< RealizationSeismicDepthSliceSettings, RealizationSeismicDepthSliceData, RealizationSeismicDepthSliceStoredData @@ -49,7 +49,7 @@ export class RealizationSeismicDepthSliceLayer } makeValueRange( - accessors: DataLayerInformationAccessors< + accessors: DataProviderInformationAccessors< RealizationSeismicDepthSliceSettings, RealizationSeismicDepthSliceData, RealizationSeismicDepthSliceStoredData diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer.ts b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicInlineProvider.ts similarity index 92% rename from frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer.ts rename to frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicInlineProvider.ts index ae1d8f693..26baf97ed 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer.ts +++ b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicInlineProvider.ts @@ -4,12 +4,12 @@ import { transformSeismicSlice, } from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; import type { - CustomDataLayerImplementation, - DataLayerInformationAccessors, + CustomDataProviderImplementation, + DataProviderInformationAccessors, FetchDataParams, -} from "@modules/_shared/LayerFramework/interfacesAndTypes/customDataLayerImplementation"; -import type { DefineDependenciesArgs } from "@modules/_shared/LayerFramework/interfacesAndTypes/customSettingsHandler"; -import { type MakeSettingTypesMap, Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; +} from "@modules/_shared/DataProviderFramework/interfacesAndTypes/customDataProviderImplementation"; +import type { DefineDependenciesArgs } from "@modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler"; +import { Setting, type MakeSettingTypesMap } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; import { isEqual } from "lodash"; @@ -30,9 +30,9 @@ export type RealizationSeismicInlineStoredData = { seismicCubeMeta: SeismicCubeMeta_api[]; }; -export class RealizationSeismicInlineLayer +export class RealizationSeismicInlineProvider implements - CustomDataLayerImplementation< + CustomDataProviderImplementation< RealizationSeismicInlineSettings, RealizationSeismicInlineData, RealizationSeismicInlineStoredData @@ -49,7 +49,7 @@ export class RealizationSeismicInlineLayer } makeValueRange( - accessors: DataLayerInformationAccessors< + accessors: DataProviderInformationAccessors< RealizationSeismicInlineSettings, RealizationSeismicInlineData, RealizationSeismicInlineStoredData diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts b/frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeDrilledWellTrajectoriesLayer.ts similarity index 83% rename from frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts rename to frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeDrilledWellTrajectoriesLayer.ts index 9e090547d..aed74af29 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer.ts +++ b/frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeDrilledWellTrajectoriesLayer.ts @@ -1,12 +1,12 @@ import type { WellboreTrajectory_api } from "@api"; import * as bbox from "@lib/utils/bbox"; -import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellTrajectoriesBoundingBox"; import { WellsLayer } from "@modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer"; import type { WellsLayerData } from "@modules/_shared/customDeckGlLayers/WellsLayer/WellsLayer"; +import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/DataProviderFramework/visualization/deckgl/boundingBoxes/makeDrilledWellTrajectoriesBoundingBox"; +import type { TransformerArgs } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; export function makeDrilledWellTrajectoriesLayer( - args: FactoryFunctionArgs, + args: TransformerArgs, ): WellsLayer | null { const { id, getData, name } = args; diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts b/frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeIntersectionGrid3dLayer.ts similarity index 88% rename from frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts rename to frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeIntersectionGrid3dLayer.ts index 365776fa3..5d88a5b0d 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer.ts +++ b/frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeIntersectionGrid3dLayer.ts @@ -1,7 +1,7 @@ -import type { IntersectionRealizationGridSettings } from "@modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer"; -import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; -import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; +import type { IntersectionRealizationGridSettings } from "@modules/_shared/DataProviderFramework/dataProviders/implementations/IntersectionRealizationGridProvider"; +import { Setting } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; +import { makeColorMapFunctionFromColorScale } from "@modules/_shared/DataProviderFramework/visualization/utils/colors"; +import type { TransformerArgs } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; import { FenceMeshSection_trans, PolylineIntersection_trans } from "@modules/_shared/utils/wellbore"; import { TGrid3DColoringMode } from "@webviz/subsurface-viewer"; import { Grid3DLayer } from "@webviz/subsurface-viewer/dist/layers"; @@ -79,7 +79,7 @@ export function makeIntersectionLayer({ name, getData, getSetting, -}: FactoryFunctionArgs): Grid3DLayer | null { +}: TransformerArgs): Grid3DLayer | null { const data = getData(); const colorScale = getSetting(Setting.COLOR_SCALE)?.colorScale; const showGridLines = getSetting(Setting.SHOW_GRID_LINES); @@ -105,7 +105,7 @@ export function makeIntersectionLayer({ data.max_grid_prop_value, ), ZIncreasingDownwards: false, - gridLines: showGridLines, + gridLines: showGridLines ?? false, material: { ambient: 0.4, diffuse: 0.7, shininess: 8, specularColor: [25, 25, 25] }, pickable: true, }); diff --git a/frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeRealizationSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeRealizationSurfaceLayer.ts new file mode 100644 index 000000000..0d9dc5b41 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeRealizationSurfaceLayer.ts @@ -0,0 +1,69 @@ +import { SurfaceDataFormat } from "@modules/2DViewer/DataProviderFramework/customDataProviderImplementations/ObservedSurfaceProvider"; +import type { + RealizationSurfaceData, + RealizationSurfaceSettings, +} from "@modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationSurfaceProvider"; +import { Setting } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; +import { makeColorMapFunctionFromColorScale } from "@modules/_shared/DataProviderFramework/visualization/utils/colors"; +import type { TransformerArgs } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; +import { MapLayer } from "@webviz/subsurface-viewer/dist/layers"; + +export function makeRealizationSurfaceLayer({ + id, + name, + getData, + getSetting, +}: TransformerArgs): MapLayer | null { + const data = getData(); + const colorScale = getSetting(Setting.COLOR_SCALE)?.colorScale; + + if (!data) { + return null; + } + + if (data.surfaceData.format === SurfaceDataFormat.FLOAT) { + return new MapLayer({ + id, + name, + meshData: data.surfaceData.valuesFloat32Arr, + frame: { + origin: [data.surfaceData.surface_def.origin_utm_x, data.surfaceData.surface_def.origin_utm_y], + count: [data.surfaceData.surface_def.npoints_x, data.surfaceData.surface_def.npoints_y], + increment: [data.surfaceData.surface_def.inc_x, data.surfaceData.surface_def.inc_y], + rotDeg: data.surfaceData.surface_def.rot_deg, + }, + valueRange: [data.surfaceData.value_min, data.surfaceData.value_max], + colorMapRange: [data.surfaceData.value_min, data.surfaceData.value_max], + colorMapFunction: makeColorMapFunctionFromColorScale( + colorScale, + data.surfaceData.value_min, + data.surfaceData.value_max, + ), + gridLines: false, + }); + } + + if (data.surfaceData.format === SurfaceDataFormat.PNG) { + return new MapLayer({ + id, + name, + meshData: data.surfaceData.png_image_base64, + frame: { + origin: [data.surfaceData.surface_def.origin_utm_x, data.surfaceData.surface_def.origin_utm_y], + count: [data.surfaceData.surface_def.npoints_x, data.surfaceData.surface_def.npoints_y], + increment: [data.surfaceData.surface_def.inc_x, data.surfaceData.surface_def.inc_y], + rotDeg: data.surfaceData.surface_def.rot_deg, + }, + valueRange: [data.surfaceData.value_min, data.surfaceData.value_max], + colorMapRange: [data.surfaceData.value_min, data.surfaceData.value_max], + colorMapFunction: makeColorMapFunctionFromColorScale( + colorScale, + data.surfaceData.value_min, + data.surfaceData.value_max, + ), + gridLines: false, + }); + } + + return null; +} diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts b/frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeSeismicFenceMeshLayer.ts similarity index 93% rename from frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts rename to frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeSeismicFenceMeshLayer.ts index e75f92fe2..b0ca68656 100644 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer.ts +++ b/frontend/src/modules/3DViewerNew/DataProviderFramework/visualization/makeSeismicFenceMeshLayer.ts @@ -4,12 +4,12 @@ import { Geometry, ShapeType, degreesToRadians } from "@lib/utils/geometry"; import { rotatePoint2Around } from "@lib/utils/vec2"; import * as vec3 from "@lib/utils/vec3"; import { SeismicSliceData_trans } from "@modules/3DViewerNew/settings/queries/queryDataTransforms"; -import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; -import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; import { SeismicFenceMeshLayer } from "@modules/_shared/customDeckGlLayers/SeismicFenceMeshLayer/SeismicFenceMeshLayer"; -import type { RealizationSeismicDepthSliceStoredData } from "../customLayerImplementations/RealizationSeismicDepthLayer"; +import type { RealizationSeismicDepthSliceStoredData } from "../customDataProviderImplementations/RealizationSeismicDepthProvider"; +import type { TransformerArgs } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; +import { Setting } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; +import { makeColorMapFunctionFromColorScale } from "@modules/_shared/DataProviderFramework/visualization/utils/colors"; export enum Plane { CROSSLINE = "CROSSLINE", @@ -20,7 +20,7 @@ export enum Plane { function predictDepthSliceGeometry({ getSetting, getStoredData, -}: FactoryFunctionArgs): Geometry | null { +}: TransformerArgs): Geometry | null { const attribute = getSetting(Setting.ATTRIBUTE); const timeOrInterval = getSetting(Setting.TIME_OR_INTERVAL); const seismicDepthSliceNumber = getSetting(Setting.SEISMIC_DEPTH_SLICE); @@ -86,7 +86,7 @@ function predictDepthSliceGeometry({ function predictCrosslineGeometry({ getSetting, getStoredData, -}: FactoryFunctionArgs): Geometry | null { +}: TransformerArgs): Geometry | null { const attribute = getSetting(Setting.ATTRIBUTE); const timeOrInterval = getSetting(Setting.TIME_OR_INTERVAL); const seismicCrosslineNumber = getSetting(Setting.SEISMIC_CROSSLINE); @@ -154,7 +154,7 @@ function predictCrosslineGeometry({ function predictInlineGeometry({ getSetting, getStoredData, -}: FactoryFunctionArgs): Geometry | null { +}: TransformerArgs): Geometry | null { const attribute = getSetting(Setting.ATTRIBUTE); const timeOrInterval = getSetting(Setting.TIME_OR_INTERVAL); const seismicInlineNumber = getSetting(Setting.SEISMIC_INLINE); @@ -221,7 +221,7 @@ function predictInlineGeometry({ export function makeSeismicFenceMeshLayerFunction(plane: Plane) { return function makeSeismicFenceMeshLayer( - args: FactoryFunctionArgs, + args: TransformerArgs, ): Layer | null { const { id, name, getData, getSetting, isLoading } = args; const data = getData(); diff --git a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts b/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts deleted file mode 100644 index 63dc054a2..000000000 --- a/frontend/src/modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { - type RealizationSurfaceData, - type RealizationSurfaceSettings, - SurfaceDataFormat, -} from "@modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer"; -import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; -import type { FactoryFunctionArgs } from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import { makeColorMapFunctionFromColorScale } from "@modules/_shared/LayerFramework/visualization/utils/colors"; -import { MapLayer } from "@webviz/subsurface-viewer/dist/layers"; - -export function makeRealizationSurfaceLayer({ - id, - name, - getData, - getSetting, -}: FactoryFunctionArgs): MapLayer | null { - const data = getData(); - const colorScale = getSetting(Setting.COLOR_SCALE)?.colorScale; - - if (!data) { - return null; - } - - if (data.surfaceData.format === SurfaceDataFormat.FLOAT) { - return new MapLayer({ - id, - name, - meshData: data.surfaceData.valuesFloat32Arr, - frame: { - origin: [data.surfaceData.surface_def.origin_utm_x, data.surfaceData.surface_def.origin_utm_y], - count: [data.surfaceData.surface_def.npoints_x, data.surfaceData.surface_def.npoints_y], - increment: [data.surfaceData.surface_def.inc_x, data.surfaceData.surface_def.inc_y], - rotDeg: data.surfaceData.surface_def.rot_deg, - }, - valueRange: [data.surfaceData.value_min, data.surfaceData.value_max], - colorMapRange: [data.surfaceData.value_min, data.surfaceData.value_max], - colorMapFunction: makeColorMapFunctionFromColorScale( - colorScale, - data.surfaceData.value_min, - data.surfaceData.value_max, - ), - gridLines: false, - }); - } - - return new MapLayer({ - id, - name, - meshData: data.surfaceData.png_image_base64, - frame: { - origin: [data.surfaceData.surface_def.origin_utm_x, data.surfaceData.surface_def.origin_utm_y], - count: [data.surfaceData.surface_def.npoints_x, data.surfaceData.surface_def.npoints_y], - increment: [data.surfaceData.surface_def.inc_x, data.surfaceData.surface_def.inc_y], - rotDeg: data.surfaceData.surface_def.rot_deg, - }, - valueRange: [data.surfaceData.value_min, data.surfaceData.value_max], - colorMapRange: [data.surfaceData.value_min, data.surfaceData.value_max], - colorMapFunction: makeColorMapFunctionFromColorScale( - colorScale, - data.surfaceData.value_min, - data.surfaceData.value_max, - ), - gridLines: false, - }); -} diff --git a/frontend/src/modules/3DViewerNew/interfaces.ts b/frontend/src/modules/3DViewerNew/interfaces.ts index af3c0ed71..64cc6ce89 100644 --- a/frontend/src/modules/3DViewerNew/interfaces.ts +++ b/frontend/src/modules/3DViewerNew/interfaces.ts @@ -1,12 +1,14 @@ import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; -import { layerManagerAtom, preferredViewLayoutAtom } from "./settings/atoms/baseAtoms"; +import { providerManagerAtom, preferredViewLayoutAtom } from "./settings/atoms/baseAtoms"; import { PreferredViewLayout } from "./types"; -import { DataLayerManager } from "../_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; +import { DataProviderManager } from "@modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager"; +import { selectedFieldIdentifierAtom } from "./settings/atoms/derivedAtoms"; export type SettingsToViewInterface = { - layerManager: DataLayerManager | null; + layerManager: DataProviderManager | null; + fieldIdentifier: string | null; preferredViewLayout: PreferredViewLayout; }; @@ -16,7 +18,10 @@ export type Interfaces = { export const settingsToViewInterfaceInitialization: InterfaceInitialization = { layerManager: (get) => { - return get(layerManagerAtom); + return get(providerManagerAtom); + }, + fieldIdentifier: (get) => { + return get(selectedFieldIdentifierAtom); }, preferredViewLayout: (get) => { return get(preferredViewLayoutAtom); diff --git a/frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts b/frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts index b50cdb3fe..6c7f4e9ed 100644 --- a/frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts +++ b/frontend/src/modules/3DViewerNew/settings/atoms/baseAtoms.ts @@ -1,8 +1,8 @@ import { PreferredViewLayout } from "@modules/2DViewer/types"; -import type { DataLayerManager } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; +import type { DataProviderManager } from "@modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager"; import { atom } from "jotai"; export const userSelectedFieldIdentifierAtom = atom(null); -export const layerManagerAtom = atom(null); +export const providerManagerAtom = atom(null); export const preferredViewLayoutAtom = atom(PreferredViewLayout.VERTICAL); diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx index b1dac82ab..5d1499fbf 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx @@ -9,28 +9,7 @@ import { MenuButton } from "@lib/components/MenuButton"; import { MenuHeading } from "@lib/components/MenuHeading"; import { MenuItem } from "@lib/components/MenuItem"; import { PreferredViewLayout } from "@modules/2DViewer/types"; -import type { LayersActionGroup } from "@modules/_shared/LayerFramework/LayersActions"; -import { type GroupDelegate, GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; -import { DataLayer } from "@modules/_shared/LayerFramework/framework/DataLayer/DataLayer"; -import type { DataLayerManager } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; -import { LayerManagerComponent } from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManagerComponent"; -import { DeltaSurface } from "@modules/_shared/LayerFramework/framework/DeltaSurface/DeltaSurface"; -import { Group } from "@modules/_shared/LayerFramework/framework/Group/Group"; -import { SettingsGroup } from "@modules/_shared/LayerFramework/framework/SettingsGroup/SettingsGroup"; -import { SharedSetting } from "@modules/_shared/LayerFramework/framework/SharedSetting/SharedSetting"; -import { GroupRegistry } from "@modules/_shared/LayerFramework/groups/GroupRegistry"; import { GroupType } from "@modules/_shared/LayerFramework/groups/groupTypes"; -import { - type Item, - type ItemGroup, - instanceofItemGroup, -} from "@modules/_shared/LayerFramework/interfacesAndTypes/entitites"; -import { LayerRegistry } from "@modules/_shared/LayerFramework/layers/LayerRegistry"; -import { ObservedSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/ObservedSurfaceLayer"; -import { RealizationSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer"; -import { StatisticalSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer"; -import { LayerType } from "@modules/_shared/LayerFramework/layers/layerTypes"; -import { Setting } from "@modules/_shared/LayerFramework/settings/settingsDefinitions"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; import { Dropdown } from "@mui/base"; import { @@ -45,9 +24,30 @@ import { import { useAtom } from "jotai"; import { preferredViewLayoutAtom } from "../atoms/baseAtoms"; +import type { DataProviderManager } from "@modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager"; +import { GroupDelegateTopic, type GroupDelegate } from "@modules/_shared/DataProviderFramework/delegates/GroupDelegate"; +import { GroupRegistry } from "@modules/_shared/DataProviderFramework/groups/GroupRegistry"; +import { DeltaSurface } from "@modules/_shared/DataProviderFramework/framework/DeltaSurface/DeltaSurface"; +import { SettingsGroup } from "@modules/_shared/DataProviderFramework/framework/SettingsGroup/SettingsGroup"; +import { SharedSetting } from "@modules/_shared/DataProviderFramework/framework/SharedSetting/SharedSetting"; +import { Setting } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; +import { DataProviderRegistry } from "@modules/_shared/DataProviderFramework/dataProviders/DataProviderRegistry"; +import { DataProviderType } from "@modules/_shared/DataProviderFramework/dataProviders/dataProviderTypes"; +import { + instanceofItemGroup, + type Item, + type ItemGroup, +} from "@modules/_shared/DataProviderFramework/interfacesAndTypes/entities"; +import { DataProvider } from "@modules/_shared/DataProviderFramework/framework/DataProvider/DataProvider"; +import { RealizationSurfaceProvider } from "@modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationSurfaceProvider"; +import { StatisticalSurfaceProvider } from "@modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider"; +import { ObservedSurfaceProvider } from "@modules/2DViewer/DataProviderFramework/customDataProviderImplementations/ObservedSurfaceProvider"; +import { Group } from "@modules/_shared/DataProviderFramework/framework/Group/Group"; +import { DataProviderManagerComponent } from "@modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManagerComponent"; +import type { ActionGroup } from "@modules/_shared/DataProviderFramework/Actions"; export type LayerManagerComponentWrapperProps = { - layerManager: DataLayerManager; + layerManager: DataProviderManager; workbenchSession: WorkbenchSession; workbenchSettings: WorkbenchSettings; }; @@ -77,48 +77,67 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper return; case "statistical-surface": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.STATISTICAL_SURFACE_3D, props.layerManager), + DataProviderRegistry.makeDataProvider(DataProviderType.STATISTICAL_SURFACE_3D, props.layerManager), ); return; case "realization-surface": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.REALIZATION_SURFACE_3D, props.layerManager), + DataProviderRegistry.makeDataProvider(DataProviderType.REALIZATION_SURFACE_3D, props.layerManager), ); return; case "realization-polygons": - groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_POLYGONS, props.layerManager)); + groupDelegate.prependChild( + DataProviderRegistry.makeDataProvider(DataProviderType.REALIZATION_POLYGONS, props.layerManager), + ); return; case "drilled-wellbore-trajectories": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.DRILLED_WELL_TRAJECTORIES, props.layerManager), + DataProviderRegistry.makeDataProvider( + DataProviderType.DRILLED_WELL_TRAJECTORIES, + props.layerManager, + ), ); return; case "drilled-wellbore-picks": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.DRILLED_WELLBORE_PICKS, props.layerManager), + DataProviderRegistry.makeDataProvider(DataProviderType.DRILLED_WELLBORE_PICKS, props.layerManager), ); return; case "intersection-realization-grid": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.INTERSECTION_REALIZATION_GRID, props.layerManager), + DataProviderRegistry.makeDataProvider( + DataProviderType.INTERSECTION_REALIZATION_GRID, + props.layerManager, + ), ); return; case "realization-grid": - groupDelegate.prependChild(LayerRegistry.makeLayer(LayerType.REALIZATION_GRID, props.layerManager)); + groupDelegate.prependChild( + DataProviderRegistry.makeDataProvider(DataProviderType.REALIZATION_GRID, props.layerManager), + ); return; case "realization-seismic-inline": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.REALIZATION_SEISMIC_INLINE, props.layerManager), + DataProviderRegistry.makeDataProvider( + DataProviderType.REALIZATION_SEISMIC_INLINE, + props.layerManager, + ), ); return; case "realization-seismic-crossline": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.REALIZATION_SEISMIC_CROSSLINE, props.layerManager), + DataProviderRegistry.makeDataProvider( + DataProviderType.REALIZATION_SEISMIC_CROSSLINE, + props.layerManager, + ), ); return; case "realization-seismic-depth-slice": groupDelegate.prependChild( - LayerRegistry.makeLayer(LayerType.REALIZATION_SEISMIC_DEPTH_SLICE, props.layerManager), + DataProviderRegistry.makeDataProvider( + DataProviderType.REALIZATION_SEISMIC_DEPTH_SLICE, + props.layerManager, + ), ); return; case "ensemble": @@ -142,11 +161,11 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper function checkIfItemMoveAllowed(movedItem: Item, destinationItem: ItemGroup): boolean { if (destinationItem instanceof DeltaSurface) { if ( - movedItem instanceof DataLayer && + movedItem instanceof DataProvider && !( - movedItem instanceof RealizationSurfaceLayer || - movedItem instanceof StatisticalSurfaceLayer || - movedItem instanceof ObservedSurfaceLayer + movedItem instanceof RealizationSurfaceProvider || + movedItem instanceof StatisticalSurfaceProvider || + movedItem instanceof ObservedSurfaceProvider ) ) { return false; @@ -156,7 +175,7 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper return false; } - if (destinationItem.getGroupDelegate().findChildren((item) => item instanceof DataLayer).length >= 2) { + if (destinationItem.getGroupDelegate().findChildren((item) => item instanceof DataProvider).length >= 2) { return false; } } @@ -170,8 +189,9 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper const adjustedLayerActions = hasView ? LAYER_ACTIONS : INITIAL_LAYER_ACTIONS; return ( - @@ -194,8 +214,8 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper } - layerActions={adjustedLayerActions} - onLayerAction={handleLayerAction} + groupActions={adjustedLayerActions} + onAction={handleLayerAction} isMoveAllowed={checkIfItemMoveAllowed} /> ); @@ -218,7 +238,7 @@ function ViewLayoutMenuItem(props: ViewLayoutMenuItemProps): React.ReactNode { ); } -const INITIAL_LAYER_ACTIONS: LayersActionGroup[] = [ +const INITIAL_LAYER_ACTIONS: ActionGroup[] = [ { label: "Groups", children: [ @@ -236,7 +256,7 @@ const INITIAL_LAYER_ACTIONS: LayersActionGroup[] = [ }, ]; -const LAYER_ACTIONS: LayersActionGroup[] = [ +const LAYER_ACTIONS: ActionGroup[] = [ { label: "Groups", children: [ diff --git a/frontend/src/modules/3DViewerNew/settings/settings.tsx b/frontend/src/modules/3DViewerNew/settings/settings.tsx index de8845451..ee9854e0c 100644 --- a/frontend/src/modules/3DViewerNew/settings/settings.tsx +++ b/frontend/src/modules/3DViewerNew/settings/settings.tsx @@ -4,25 +4,24 @@ import { ModuleSettingsProps } from "@framework/Module"; import { useEnsembleSet } from "@framework/WorkbenchSession"; import { FieldDropdown } from "@framework/components/FieldDropdown"; import { CollapsibleGroup } from "@lib/components/CollapsibleGroup"; -import { GroupDelegateTopic } from "@modules/_shared/LayerFramework/delegates/GroupDelegate"; import { useQueryClient } from "@tanstack/react-query"; import { useAtom, useAtomValue, useSetAtom } from "jotai"; -import { layerManagerAtom, preferredViewLayoutAtom, userSelectedFieldIdentifierAtom } from "./atoms/baseAtoms"; +import { providerManagerAtom, preferredViewLayoutAtom, userSelectedFieldIdentifierAtom } from "./atoms/baseAtoms"; import { selectedFieldIdentifierAtom } from "./atoms/derivedAtoms"; import { LayerManagerComponentWrapper } from "./components/layerManagerComponentWrapper"; - import { - DataLayerManager, - LayerManagerTopic, -} from "../../_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; + DataProviderManager, + DataProviderManagerTopic, +} from "@modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager"; +import { GroupDelegateTopic } from "@modules/_shared/DataProviderFramework/delegates/GroupDelegate"; export function Settings(props: ModuleSettingsProps): React.ReactNode { const ensembleSet = useEnsembleSet(props.workbenchSession); const queryClient = useQueryClient(); - const [layerManager, setLayerManager] = useAtom(layerManagerAtom); + const [layerManager, setLayerManager] = useAtom(providerManagerAtom); const fieldIdentifier = useAtomValue(selectedFieldIdentifierAtom); const setFieldIdentifier = useSetAtom(userSelectedFieldIdentifierAtom); @@ -48,7 +47,7 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode { ); const applyPersistedState = React.useCallback( - function applyPersistedState(layerManager: DataLayerManager) { + function applyPersistedState(layerManager: DataProviderManager) { const serializedState = window.localStorage.getItem( `${props.settingsContext.getInstanceIdString()}-settings`, ); @@ -76,7 +75,11 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode { React.useEffect( function onMountEffect() { - const newLayerManager = new DataLayerManager(props.workbenchSession, props.workbenchSettings, queryClient); + const newLayerManager = new DataProviderManager( + props.workbenchSession, + props.workbenchSettings, + queryClient, + ); setLayerManager(newLayerManager); applyPersistedState(newLayerManager); @@ -98,7 +101,7 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode { const unsubscribeDataRev = layerManager .getPublishSubscribeDelegate() - .makeSubscriberFunction(LayerManagerTopic.LAYER_DATA_REVISION)(persistState); + .makeSubscriberFunction(DataProviderManagerTopic.DATA_REVISION)(persistState); const unsubscribeExpands = layerManager .getGroupDelegate() diff --git a/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx index 0ba9f8fcf..6d77c64b1 100644 --- a/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/InteractionWrapper.tsx @@ -10,22 +10,24 @@ import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; import { converter } from "culori"; import { ContextMenu } from "./ContextMenu"; -import { ReadooutWrapperProps, ReadoutWrapper } from "./ReadoutWrapper"; +import { ReadoutWrapperProps, ReadoutWrapper } from "./ReadoutWrapper"; import { Toolbar } from "./Toolbar"; import { DeckGlInstanceManager, DeckGlInstanceManagerTopic } from "../utils/DeckGlInstanceManager"; import { Polyline, PolylinesPlugin, PolylinesPluginTopic } from "../utils/PolylinesPlugin"; export type InteractionWrapperProps = Omit< - ReadooutWrapperProps, + ReadoutWrapperProps, "deckGlManager" | "triggerHome" | "verticalScale" | "deckGlRef" ->; +> & { + fieldIdentifier: string | null; +}; export function InteractionWrapper(props: InteractionWrapperProps): React.ReactNode { const deckGlRef = React.useRef(null); deckGlRef.current?.deck?.needsRedraw; const [deckGlManager, setDeckGlManager] = React.useState( - new DeckGlInstanceManager(deckGlRef.current) + new DeckGlInstanceManager(deckGlRef.current), ); const [polylinesPlugin, setPolylinesPlugin] = React.useState(new PolylinesPlugin(deckGlManager)); @@ -49,7 +51,7 @@ export function InteractionWrapper(props: InteractionWrapperProps): React.ReactN i++; } }, - [colorSet] + [colorSet], ); React.useEffect( @@ -65,8 +67,10 @@ export function InteractionWrapper(props: InteractionWrapperProps): React.ReactN const unsubscribeFromPolylinesPlugin = polylinesPlugin .getPublishSubscribeDelegate() .makeSubscriberFunction(PolylinesPluginTopic.EDITING_POLYLINE_ID)(() => { - if (polylinesPlugin.getCurrentEditingPolylineId() === null) { - intersectionPolylines.setPolylines(polylinesPlugin.getPolylines()); + if (polylinesPlugin.getCurrentEditingPolylineId() === null && props.fieldIdentifier !== null) { + intersectionPolylines.setPolylines( + polylinesPlugin.getPolylines().map((p) => ({ ...p, fieldId: props.fieldIdentifier! })), + ); } }); @@ -74,7 +78,7 @@ export function InteractionWrapper(props: InteractionWrapperProps): React.ReactN IntersectionPolylinesEvent.CHANGE, () => { polylinesPlugin.setPolylines(intersectionPolylines.getPolylines()); - } + }, ); return function cleanupDeckGlManager() { @@ -83,7 +87,7 @@ export function InteractionWrapper(props: InteractionWrapperProps): React.ReactN unsubscribeFromIntersectionPolylines(); }; }, - [intersectionPolylines, colorGenerator] + [intersectionPolylines, colorGenerator], ); const [triggerHomeCounter, setTriggerHomeCounter] = React.useState(0); @@ -121,10 +125,10 @@ export function InteractionWrapper(props: InteractionWrapperProps): React.ReactN } return polyline; - }) + }), ); }, - [activePolylineId] + [activePolylineId], ); let adjustedLayers: DeckGlLayer[] = [...props.layers]; diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 9761116c3..8fcade128 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { View as DeckGlView } from "@deck.gl/core"; +import { View as DeckGlView, type Layer } from "@deck.gl/core"; import type { ViewContext } from "@framework/ModuleContext"; import { useViewStatusWriter } from "@framework/StatusWriter"; import type { WorkbenchSession } from "@framework/WorkbenchSession"; @@ -9,109 +9,124 @@ import { useElementSize } from "@lib/hooks/useElementSize"; import * as bbox from "@lib/utils/boundingBox"; import type { Interfaces } from "@modules/2DViewer/interfaces"; import { PreferredViewLayout } from "@modules/2DViewer/types"; -import { RealizationSeismicCrosslineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicCrosslineLayer"; -import { RealizationSeismicDepthSliceLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicDepthLayer"; -import { RealizationSeismicInlineLayer } from "@modules/3DViewerNew/LayerFramework/customLayerImplementations/RealizationSeismicInlineLayer"; -import { makeDrilledWellTrajectoriesLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeDrilledWellTrajectoriesLayer"; -import { makeIntersectionLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeIntersectionGrid3dLayer"; -import { makeRealizationSurfaceLayer } from "@modules/3DViewerNew/LayerFramework/visualization/makeRealizationSurfaceLayer"; +import { makeDrilledWellTrajectoriesLayer } from "@modules/3DViewerNew/DataProviderFramework/visualization/makeDrilledWellTrajectoriesLayer"; +import { makeIntersectionLayer } from "@modules/3DViewerNew/DataProviderFramework/visualization/makeIntersectionGrid3dLayer"; +import { makeRealizationSurfaceLayer } from "@modules/3DViewerNew/DataProviderFramework/visualization/makeRealizationSurfaceLayer"; import { Plane, makeSeismicFenceMeshLayerFunction, -} from "@modules/3DViewerNew/LayerFramework/visualization/makeSeismicFenceMeshLayer"; -import { - type DataLayerManager, - LayerManagerTopic, -} from "@modules/_shared/LayerFramework/framework/DataLayerManager/DataLayerManager"; -import { GroupType } from "@modules/_shared/LayerFramework/groups/groupTypes"; -import { View } from "@modules/_shared/LayerFramework/groups/implementations/View"; -import { DrilledWellTrajectoriesLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellTrajectoriesLayer"; -import { DrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/layers/implementations/DrilledWellborePicksLayer"; -import { IntersectionRealizationGridLayer } from "@modules/_shared/LayerFramework/layers/implementations/IntersectionRealizationGridLayer"; -import { RealizationGridLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationGridLayer"; -import { RealizationPolygonsLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationPolygonsLayer"; -import { RealizationSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/RealizationSurfaceLayer"; -import { StatisticalSurfaceLayer } from "@modules/_shared/LayerFramework/layers/implementations/StatisticalSurfaceLayer"; -import { LayerType } from "@modules/_shared/LayerFramework/layers/layerTypes"; -import { - type Annotation, - type LayerWithPosition, - VisualizationFactory, - type VisualizationTarget, -} from "@modules/_shared/LayerFramework/visualization/VisualizationFactory"; -import { makeColorScaleAnnotation } from "@modules/_shared/LayerFramework/visualization/deckgl/annotations/makeColorScaleAnnotation"; -import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellTrajectoriesBoundingBox"; -import { makeDrilledWellborePicksBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makeDrilledWellborePicksBoundingBox"; -import { makePolygonDataBoundingBox } from "@modules/_shared/LayerFramework/visualization/deckgl/boundingBoxes/makePolygonDataBoundingBox"; -import { makeDrilledWellborePicksLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeDrilledWellborePicksLayer"; -import { makeRealizationGridLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationGridLayer"; -import { makeRealizationPolygonsLayer } from "@modules/_shared/LayerFramework/visualization/deckgl/makeRealizationPolygonsLayer"; +} from "@modules/3DViewerNew/DataProviderFramework/visualization/makeSeismicFenceMeshLayer"; import { ColorLegendsContainer } from "@modules/_shared/components/ColorLegendsContainer"; import { usePublishSubscribeTopicValue } from "@modules/_shared/utils/PublishSubscribeDelegate"; -import type { BoundingBox3D, ViewportType } from "@webviz/subsurface-viewer"; -import type { ViewsType } from "@webviz/subsurface-viewer/dist/SubsurfaceViewer"; +import type { ViewportType } from "@webviz/subsurface-viewer"; import { AxesLayer } from "@webviz/subsurface-viewer/dist/layers"; import { InteractionWrapper } from "./InteractionWrapper"; import { PlaceholderLayer } from "../../../_shared/customDeckGlLayers/PlaceholderLayer"; +import { + VisualizationAssembler, + VisualizationItemType, + type VisualizationTarget, +} from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; +import { DataProviderType } from "@modules/_shared/DataProviderFramework/dataProviders/dataProviderTypes"; +import { RealizationSurfaceProvider } from "@modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationSurfaceProvider"; +import { makeColorScaleAnnotation } from "@modules/2DViewer/DataProviderFramework/annotations/makeColorScaleAnnotation"; +import { StatisticalSurfaceProvider } from "@modules/2DViewer/DataProviderFramework/customDataProviderImplementations/StatisticalSurfaceProvider"; +import { RealizationPolygonsProvider } from "@modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationPolygonsProvider"; +import { makeRealizationPolygonsLayer } from "@modules/2DViewer/DataProviderFramework/visualization/makeRealizationPolygonsLayer"; +import { IntersectionRealizationGridProvider } from "@modules/_shared/DataProviderFramework/dataProviders/implementations/IntersectionRealizationGridProvider"; +import { RealizationGridProvider } from "@modules/2DViewer/DataProviderFramework/customDataProviderImplementations/RealizationGridProvider"; +import { makeRealizationGridLayer } from "@modules/2DViewer/DataProviderFramework/visualization/makeRealizationGridLayer"; +import { DrilledWellborePicksProvider } from "@modules/_shared/DataProviderFramework/dataProviders/implementations/DrilledWellborePicksProvider"; +import { makeDrilledWellborePicksLayer } from "@modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellborePicksLayer"; +import { DrilledWellTrajectoriesProvider } from "@modules/_shared/DataProviderFramework/dataProviders/implementations/DrilledWellTrajectoriesProvider"; +import { RealizationSeismicDepthSliceProvider } from "@modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicDepthProvider"; +import { RealizationSeismicInlineProvider } from "@modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicInlineProvider"; +import { RealizationSeismicCrosslineProvider } from "@modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/RealizationSeismicCrosslineProvider"; +import { + DataProviderManagerTopic, + type DataProviderManager, +} from "@modules/_shared/DataProviderFramework/framework/DataProviderManager/DataProviderManager"; +import { GroupType } from "@modules/_shared/DataProviderFramework/groups/groupTypes"; +import { makeStatisticalSurfaceLayer } from "@modules/2DViewer/DataProviderFramework/visualization/makeStatisticalSurfaceLayer"; -const VISUALIZATION_FACTORY = new VisualizationFactory(); +const VISUALIZATION_ASSEMBLER = new VisualizationAssembler(); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_SURFACE_3D, RealizationSurfaceLayer, { - makeVisualizationFunction: makeRealizationSurfaceLayer, - makeAnnotationsFunction: makeColorScaleAnnotation, -}); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.STATISTICAL_SURFACE_3D, StatisticalSurfaceLayer, { - makeVisualizationFunction: makeRealizationSurfaceLayer, - makeAnnotationsFunction: makeColorScaleAnnotation, -}); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_POLYGONS, RealizationPolygonsLayer, { - makeVisualizationFunction: makeRealizationPolygonsLayer, - calculateBoundingBoxFunction: makePolygonDataBoundingBox, -}); -VISUALIZATION_FACTORY.registerLayerFunctions( - LayerType.INTERSECTION_REALIZATION_GRID, - IntersectionRealizationGridLayer, +VISUALIZATION_ASSEMBLER.registerDataProviderTransformers( + DataProviderType.REALIZATION_SURFACE_3D, + RealizationSurfaceProvider, { - makeVisualizationFunction: makeIntersectionLayer, - makeAnnotationsFunction: makeColorScaleAnnotation, + transformToVisualization: makeRealizationSurfaceLayer, + transformToAnnotations: makeColorScaleAnnotation, }, ); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_GRID, RealizationGridLayer, { - makeVisualizationFunction: makeRealizationGridLayer, - makeAnnotationsFunction: makeColorScaleAnnotation, -}); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.DRILLED_WELLBORE_PICKS, DrilledWellborePicksLayer, { - makeVisualizationFunction: makeDrilledWellborePicksLayer, - calculateBoundingBoxFunction: makeDrilledWellborePicksBoundingBox, -}); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.DRILLED_WELL_TRAJECTORIES, DrilledWellTrajectoriesLayer, { - makeVisualizationFunction: makeDrilledWellTrajectoriesLayer, - calculateBoundingBoxFunction: makeDrilledWellTrajectoriesBoundingBox, -}); -VISUALIZATION_FACTORY.registerLayerFunctions( - LayerType.REALIZATION_SEISMIC_DEPTH_SLICE, - RealizationSeismicDepthSliceLayer, +VISUALIZATION_ASSEMBLER.registerDataProviderTransformers( + DataProviderType.STATISTICAL_SURFACE_3D, + StatisticalSurfaceProvider, + { + transformToVisualization: makeStatisticalSurfaceLayer, + transformToAnnotations: makeColorScaleAnnotation, + }, +); +VISUALIZATION_ASSEMBLER.registerDataProviderTransformers( + DataProviderType.REALIZATION_POLYGONS, + RealizationPolygonsProvider, { - makeVisualizationFunction: makeSeismicFenceMeshLayerFunction(Plane.DEPTH), + transformToVisualization: makeRealizationPolygonsLayer, }, ); -VISUALIZATION_FACTORY.registerLayerFunctions(LayerType.REALIZATION_SEISMIC_INLINE, RealizationSeismicInlineLayer, { - makeVisualizationFunction: makeSeismicFenceMeshLayerFunction(Plane.INLINE), +VISUALIZATION_ASSEMBLER.registerDataProviderTransformers( + DataProviderType.INTERSECTION_REALIZATION_GRID, + IntersectionRealizationGridProvider, + { + transformToVisualization: makeIntersectionLayer, + transformToAnnotations: makeColorScaleAnnotation, + }, +); +VISUALIZATION_ASSEMBLER.registerDataProviderTransformers(DataProviderType.REALIZATION_GRID, RealizationGridProvider, { + transformToVisualization: makeRealizationGridLayer, + transformToAnnotations: makeColorScaleAnnotation, }); -VISUALIZATION_FACTORY.registerLayerFunctions( - LayerType.REALIZATION_SEISMIC_CROSSLINE, - RealizationSeismicCrosslineLayer, +VISUALIZATION_ASSEMBLER.registerDataProviderTransformers( + DataProviderType.DRILLED_WELLBORE_PICKS, + DrilledWellborePicksProvider, { - makeVisualizationFunction: makeSeismicFenceMeshLayerFunction(Plane.CROSSLINE), + transformToVisualization: makeDrilledWellborePicksLayer, + }, +); +VISUALIZATION_ASSEMBLER.registerDataProviderTransformers( + DataProviderType.DRILLED_WELL_TRAJECTORIES, + DrilledWellTrajectoriesProvider, + { + transformToVisualization: makeDrilledWellTrajectoriesLayer, + }, +); +VISUALIZATION_ASSEMBLER.registerDataProviderTransformers( + DataProviderType.REALIZATION_SEISMIC_DEPTH_SLICE, + RealizationSeismicDepthSliceProvider, + { + transformToVisualization: makeSeismicFenceMeshLayerFunction(Plane.DEPTH), + }, +); +VISUALIZATION_ASSEMBLER.registerDataProviderTransformers( + DataProviderType.REALIZATION_SEISMIC_INLINE, + RealizationSeismicInlineProvider, + { + transformToVisualization: makeSeismicFenceMeshLayerFunction(Plane.INLINE), + }, +); +VISUALIZATION_ASSEMBLER.registerDataProviderTransformers( + DataProviderType.REALIZATION_SEISMIC_CROSSLINE, + RealizationSeismicCrosslineProvider, + { + transformToVisualization: makeSeismicFenceMeshLayerFunction(Plane.CROSSLINE), }, ); - -VISUALIZATION_FACTORY.registerViewFunction(GroupType.VIEW, View, ({ getSetting }) => ({ test: "test" })); export type LayersWrapperProps = { - layerManager: DataLayerManager; + fieldIdentifier: string | null; + layerManager: DataProviderManager; preferredViewLayout: PreferredViewLayout; viewContext: ViewContext; workbenchSession: WorkbenchSession; @@ -125,110 +140,116 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { const mainDivSize = useElementSize(mainDivRef); const statusWriter = useViewStatusWriter(props.viewContext); - usePublishSubscribeTopicValue(props.layerManager, LayerManagerTopic.LAYER_DATA_REVISION); + usePublishSubscribeTopicValue(props.layerManager, DataProviderManagerTopic.DATA_REVISION); const viewports: ViewportType[] = []; - const viewerLayers: LayerWithPosition[] = []; + const deckGlLayers: Layer[] = []; const viewportAnnotations: React.ReactNode[] = []; - const globalAnnotations: Annotation[] = []; + const globalLayerIds: string[] = ["placeholder"]; - const views: ViewsType = { - layout: [1, 1], - viewports: viewports, - showLabel: false, - }; + let numLoadingLayers = 0; - let numCols = 0; - let numRows = 0; + const assemblerProduct = VISUALIZATION_ASSEMBLER.make(props.layerManager); - const factoryProduct = VISUALIZATION_FACTORY.make(props.layerManager); + const globalAnnotations = assemblerProduct.annotations; - numCols = Math.ceil(Math.sqrt(factoryProduct.views.length)); - numRows = Math.ceil(factoryProduct.views.length / numCols); + const numViews = assemblerProduct.children.filter( + (item) => item.itemType === VisualizationItemType.GROUP && item.groupType === GroupType.VIEW, + ).length; - if (props.preferredViewLayout === PreferredViewLayout.HORIZONTAL) { - [numCols, numRows] = [numRows, numCols]; - } + let numCols = Math.ceil(Math.sqrt(numViews)); + let numRows = Math.ceil(numViews / numCols); - views.layout = [numCols, numRows]; - - viewerLayers.push(...factoryProduct.layers); - globalAnnotations.push(...factoryProduct.annotations); - const globalLayerIds = factoryProduct.layers.map((layer) => layer.layer.id); - - for (const view of factoryProduct.views) { - viewports.push({ - id: view.id, - name: view.name, - isSync: true, - show3D: true, - layerIds: [ - ...globalLayerIds, - ...view.layers.map((layer) => layer.layer.id), - "placeholder", - "axes-layer", - "editable-polylines-layer", - "polylines-layer", - "hover-point-layer", - ], - }); - viewerLayers.push(...view.layers); - - viewportAnnotations.push( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - /* @ts-expect-error */ - - "colorScale" in el), ...globalAnnotations]} - height={((mainDivSize.height / 3) * 2) / numCols - 20} - position="left" - /> -
-
-
-
{view.name}
+ for (const item of assemblerProduct.children) { + if (item.itemType === VisualizationItemType.GROUP && item.groupType === GroupType.VIEW) { + const layerIds: string[] = []; + for (const child of item.children) { + if (child.itemType === VisualizationItemType.DATA_PROVIDER_VISUALIZATION) { + const layer = child.visualization; + layerIds.push(layer.id); + deckGlLayers.push(layer); + } + } + viewports.push({ + id: item.id, + name: item.name, + isSync: true, + layerIds, + }); + + viewportAnnotations.push( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + /* @ts-expect-error */ + + "colorScale" in el), ...globalAnnotations]} + height={((mainDivSize.height / 3) * 2) / numCols - 20} + position="left" + /> +
+
+
+
{item.name}
+
-
-
, - ); + , + ); + } else if (item.itemType === VisualizationItemType.DATA_PROVIDER_VISUALIZATION) { + deckGlLayers.push(item.visualization); + globalLayerIds.push(item.visualization.id); + } } - if (factoryProduct.combinedBoundingBox !== null) { + if (props.preferredViewLayout === PreferredViewLayout.HORIZONTAL) { + [numCols, numRows] = [numRows, numCols]; + } + + if (assemblerProduct.combinedBoundingBox !== null) { if (prevBoundingBox !== null) { - if (!bbox.outerBoxcontainsInnerBox(prevBoundingBox, factoryProduct.combinedBoundingBox)) { - setPrevBoundingBox(factoryProduct.combinedBoundingBox); + if (!bbox.outerBoxcontainsInnerBox(prevBoundingBox, assemblerProduct.combinedBoundingBox)) { + setPrevBoundingBox(assemblerProduct.combinedBoundingBox); } } else { - setPrevBoundingBox(factoryProduct.combinedBoundingBox); + setPrevBoundingBox(assemblerProduct.combinedBoundingBox); } } - statusWriter.setLoading(factoryProduct.numLoadingLayers > 0); + numLoadingLayers = assemblerProduct.numLoadingDataProviders; + statusWriter.setLoading(assemblerProduct.numLoadingDataProviders > 0); - for (const message of factoryProduct.errorMessages) { + for (const message of assemblerProduct.aggregatedErrorMessages) { statusWriter.addError(message); } - let bounds: BoundingBox3D | undefined = undefined; - if (prevBoundingBox) { - bounds = bbox.toNumArray(prevBoundingBox); - } + deckGlLayers.push(new PlaceholderLayer({ id: "placeholder" })); + deckGlLayers.push( + new AxesLayer({ + id: "axes-layer", + visible: true, + ZIncreasingDownwards: true, + }), + ); - const layers = viewerLayers.toSorted((a, b) => b.position - a.position).map((layer) => layer.layer); - layers.push(new PlaceholderLayer({ id: "placeholder" })); - layers.push(new AxesLayer({ id: "axes-layer", visible: true, ZIncreasingDownwards: true, bounds })); + deckGlLayers.reverse(); return (
({ + ...viewport, + layerIds: [...(viewport.layerIds ?? []), ...globalLayerIds], + })), + showLabel: false, + }} viewportAnnotations={viewportAnnotations} - layers={layers} - bounds={bounds} + layers={deckGlLayers} workbenchSession={props.workbenchSession} workbenchSettings={props.workbenchSettings} /> diff --git a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx index 2d5c7a910..418f6cecf 100644 --- a/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/ReadoutWrapper.tsx @@ -11,7 +11,7 @@ import { ReadoutBoxWrapper } from "./ReadoutBoxWrapper"; import { DeckGlInstanceManager } from "../utils/DeckGlInstanceManager"; -export type ReadooutWrapperProps = { +export type ReadoutWrapperProps = { views: ViewsType; viewportAnnotations: React.ReactNode[]; layers: DeckGlLayer[]; @@ -24,7 +24,7 @@ export type ReadooutWrapperProps = { deckGlRef: React.RefObject; }; -export function ReadoutWrapper(props: ReadooutWrapperProps): React.ReactNode { +export function ReadoutWrapper(props: ReadoutWrapperProps): React.ReactNode { const id = React.useId(); const deckGlRef = React.useRef(null); diff --git a/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.tsx b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.tsx index e94e98ddc..65e45d7a6 100644 --- a/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.tsx +++ b/frontend/src/modules/3DViewerNew/view/utils/PolylinesPlugin.tsx @@ -97,11 +97,11 @@ export class PolylinesPlugin extends DeckGlPlugin implements PublishSubscribe polyline.id !== this._currentEditingPolylineId), selectedPolylineId: - this._editingMode === PolylineEditingMode.NONE ? undefined : this._selectedPolylineId ?? undefined, + this._editingMode === PolylineEditingMode.NONE + ? undefined + : (this._selectedPolylineId ?? undefined), hoverable: this._editingMode === PolylineEditingMode.IDLE, }), ]; @@ -492,12 +494,12 @@ export class PolylinesPlugin extends DeckGlPlugin implements PublishSubscribe): React.ReactNode { const preferredViewLayout = props.viewContext.useSettingsToViewInterfaceValue("preferredViewLayout"); const layerManager = props.viewContext.useSettingsToViewInterfaceValue("layerManager"); + const fieldIdentifier = props.viewContext.useSettingsToViewInterfaceValue("fieldIdentifier"); if (!layerManager) { return null; @@ -16,6 +17,7 @@ export function View(props: ModuleViewProps): React.ReactNode { return ( { const wellboreHeaders = getHelperDependency(wellboreHeadersDep); @@ -114,10 +112,7 @@ export class DrilledWellTrajectoriesProvider return []; } - return wellboreHeaders.map((header) => ({ - wellboreUuid: header.wellboreUuid, - uniqueWellboreIdentifier: header.uniqueWellboreIdentifier, - })); + return wellboreHeaders; }); } } diff --git a/frontend/src/modules/_shared/DataProviderFramework/dataProviders/implementations/DrilledWellborePicksProvider.ts b/frontend/src/modules/_shared/DataProviderFramework/dataProviders/implementations/DrilledWellborePicksProvider.ts index 283b6466b..b50c4b8b1 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/dataProviders/implementations/DrilledWellborePicksProvider.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/dataProviders/implementations/DrilledWellborePicksProvider.ts @@ -9,7 +9,6 @@ import { import type { MakeSettingTypesMap } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; import { Setting } from "@modules/_shared/DataProviderFramework/settings/settingsDefinitions"; - import type { CustomDataProviderImplementation, DataProviderInformationAccessors, @@ -60,11 +59,9 @@ export class DrilledWellborePicksProvider registerQueryKey(queryOptions.queryKey); - const promise = queryClient - .fetchQuery(queryOptions) - .then((response: WellborePick_api[]) => { - return response.filter((trajectory) => selectedWellboreUuids.includes(trajectory.wellboreUuid)); - }); + const promise = queryClient.fetchQuery(queryOptions).then((response: WellborePick_api[]) => { + return response.filter((trajectory) => selectedWellboreUuids.includes(trajectory.wellboreUuid)); + }); return promise; } @@ -84,7 +81,6 @@ export class DrilledWellborePicksProvider defineDependencies({ helperDependency, availableSettingsUpdater, - storedDataUpdater, workbenchSession, queryClient, }: DefineDependenciesArgs) { @@ -154,12 +150,7 @@ export class DrilledWellborePicksProvider return []; } - return wellboreHeaders.map((header) => { - return { - wellboreUuid: header.wellboreUuid, - uniqueWellboreIdentifier: header.uniqueWellboreIdentifier, - }; - }); + return wellboreHeaders; }); availableSettingsUpdater(Setting.SURFACE_NAME, ({ getHelperDependency }) => { diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts index 72da16b92..862ba048b 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/SettingsContextDelegate.ts @@ -249,9 +249,6 @@ export class SettingsContextDelegate< if (topic === SettingsContextDelegateTopic.SETTINGS_AND_STORED_DATA_CHANGED) { return; } - if (topic === SettingsContextDelegateTopic.STORED_DATA_CHANGED) { - return; - } if (topic === SettingsContextDelegateTopic.STATUS) { return this._status; } diff --git a/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts b/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts index 135969d7b..ca769ac98 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/delegates/_utils/Dependency.ts @@ -268,10 +268,6 @@ export class Dependency< return; } - if (newValue === CancelUpdate) { - return; - } - this.applyNewValue(newValue); } diff --git a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts index e1bd15cb5..c2e28aa8a 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/interfacesAndTypes/customSettingsHandler.ts @@ -34,7 +34,7 @@ export interface UpdateFunc< getGlobalSetting: (settingName: T) => GlobalSettings[T]; getHelperDependency: GetHelperDependency; abortSignal: AbortSignal; - }): TReturnValue | typeof CancelUpdate; + }): TReturnValue; } export interface DefineBasicDependenciesArgs< diff --git a/frontend/src/modules/_shared/DataProviderFramework/settings/implementations/DrilledWellboresSetting.tsx b/frontend/src/modules/_shared/DataProviderFramework/settings/implementations/DrilledWellboresSetting.tsx index 53929bc5f..755bb832e 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/settings/implementations/DrilledWellboresSetting.tsx +++ b/frontend/src/modules/_shared/DataProviderFramework/settings/implementations/DrilledWellboresSetting.tsx @@ -14,7 +14,7 @@ import type { import type { MakeAvailableValuesTypeBasedOnCategory } from "../../interfacesAndTypes/utils"; import type { SettingCategory } from "../settingsDefinitions"; -type ValueType = Pick[] | null; +type ValueType = WellboreHeader_api[] | null; export class DrilledWellboresSetting implements CustomSettingImplementation { defaultValue: ValueType = null; From e1c4a68d72b82cc4d77cd2733caa65c7cddbfa07 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 9 May 2025 16:46:09 +0200 Subject: [PATCH 94/97] fix: resetting shared settings --- .../ExternalSettingController.ts | 19 ++++++++++++++++++- .../SettingManager/SettingManager.ts | 7 ++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts index 9ddad2c42..afc2e342b 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/ExternalSettingController/ExternalSettingController.ts @@ -57,7 +57,9 @@ export class ExternalSettingController< } private updateControlledSettings(): void { - this.unregisterAllControlledSettings(); + const oldControlledSettings = new Map(this._controlledSettings); + this._controlledSettings.clear(); + this._availableValuesMap.clear(); let parentGroup = this._parentItem.getItemDelegate().getParentGroup(); if (this._parentItem instanceof Group) { @@ -82,6 +84,15 @@ export class ExternalSettingController< } } + for (const settingId of oldControlledSettings.keys()) { + if (!this._controlledSettings.has(settingId)) { + const setting = oldControlledSettings.get(settingId); + if (setting) { + setting.unregisterExternalSettingController(); + } + } + } + if (this._controlledSettings.size === 0) { this._setting.setAvailableValues(null); return; @@ -109,6 +120,12 @@ export class ExternalSettingController< } makeIntersectionOfAvailableValues(): void { + for (const setting of this._controlledSettings.values()) { + if (!setting.isInitialized(true)) { + return; + } + } + const category = this._setting.getCategory(); const reducerDefinition = settingCategoryAvailableValuesIntersectionReducerMap[category]; diff --git a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts index 63042c6ed..7b00cd042 100644 --- a/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts +++ b/frontend/src/modules/_shared/DataProviderFramework/framework/SettingManager/SettingManager.ts @@ -320,11 +320,12 @@ export class SettingManager< return; } this._initialized = true; + this._publishSubscribeDelegate.notifySubscribers(SettingTopic.IS_INITIALIZED); } - isInitialized(): boolean { - if (this._externalController) { + isInitialized(itself: boolean = false): boolean { + if (this._externalController && !itself) { return this._externalController.getSetting().isInitialized(); } return this._initialized || this._isStatic; @@ -488,9 +489,9 @@ export class SettingManager< setAvailableValues(availableValues: AvailableValuesType | null): void { if (this._externalController) { + this._availableValues = availableValues; this.initialize(); this._externalController.setAvailableValues(this.getId(), availableValues); - this._availableValues = availableValues; return; } From ca7a67dd79fed9fe3af376dc9f0ebf4e536c8291 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 12 May 2025 10:50:38 +0200 Subject: [PATCH 95/97] wip --- frontend/src/modules/3DViewer/preview.webp | Bin 52254 -> 0 bytes .../dataProviderTypes.ts | 5 ++ .../registerAllDataProviders.ts | 19 ++++++ frontend/src/modules/3DViewerNew/preview.jpg | Bin 0 -> 5071 bytes frontend/src/modules/3DViewerNew/preview.tsx | 6 +- frontend/src/modules/3DViewerNew/preview.webp | Bin 52254 -> 0 bytes ...> dataProviderManagerComponentWrapper.tsx} | 64 ++++++++++++------ .../modules/3DViewerNew/settings/settings.tsx | 2 +- .../view/components/LayersWrapper.tsx | 1 + 9 files changed, 70 insertions(+), 27 deletions(-) delete mode 100644 frontend/src/modules/3DViewer/preview.webp create mode 100644 frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/dataProviderTypes.ts create mode 100644 frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/registerAllDataProviders.ts create mode 100644 frontend/src/modules/3DViewerNew/preview.jpg delete mode 100644 frontend/src/modules/3DViewerNew/preview.webp rename frontend/src/modules/3DViewerNew/settings/components/{layerManagerComponentWrapper.tsx => dataProviderManagerComponentWrapper.tsx} (93%) diff --git a/frontend/src/modules/3DViewer/preview.webp b/frontend/src/modules/3DViewer/preview.webp deleted file mode 100644 index d180acb38d4be2486e7125d9adddd4b771a921cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52254 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 Td5qy!>Iqlnl?KhTd;kCd!im;n diff --git a/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/dataProviderTypes.ts b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/dataProviderTypes.ts new file mode 100644 index 000000000..53e1162e7 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/dataProviderTypes.ts @@ -0,0 +1,5 @@ +export enum CustomDataProviderType { + REALIZATION_SEISMIC_CROSSLINE = "REALIZATION_SEISMIC_CROSSLINE", + REALIZATION_SEISMIC_INLINE = "REALIZATION_SEISMIC_INLINE", + REALIZATION_SEISMIC_DEPTH_SLICE = "REALIZATION_SEISMIC_DEPTH_SLICE", +} diff --git a/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/registerAllDataProviders.ts b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/registerAllDataProviders.ts new file mode 100644 index 000000000..ee7d3be41 --- /dev/null +++ b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/registerAllDataProviders.ts @@ -0,0 +1,19 @@ +import { DataProviderRegistry } from "@modules/_shared/DataProviderFramework/dataProviders/DataProviderRegistry"; + +import { CustomDataProviderType } from "./dataProviderTypes"; +import { RealizationSeismicCrosslineProvider } from "./RealizationSeismicCrosslineProvider"; +import { RealizationSeismicInlineProvider } from "./RealizationSeismicInlineProvider"; +import { RealizationSeismicDepthSliceProvider } from "./RealizationSeismicDepthProvider"; + +DataProviderRegistry.registerDataProvider( + CustomDataProviderType.REALIZATION_SEISMIC_CROSSLINE, + RealizationSeismicCrosslineProvider, +); +DataProviderRegistry.registerDataProvider( + CustomDataProviderType.REALIZATION_SEISMIC_INLINE, + RealizationSeismicInlineProvider, +); +DataProviderRegistry.registerDataProvider( + CustomDataProviderType.REALIZATION_SEISMIC_DEPTH_SLICE, + RealizationSeismicDepthSliceProvider, +); diff --git a/frontend/src/modules/3DViewerNew/preview.jpg b/frontend/src/modules/3DViewerNew/preview.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d1e2273e919ec0ec2738e563020abfc4e65e9fbb GIT binary patch literal 5071 zcmY*bcQo8xxBd+?dK(N0F``5ry+#tfBzheZEr>QslxPtWL>&epS_GqaAx0OG=%X9G zMvs;dAw;>}@4a`e?>_s#z0clf?`NO2o^v^MxdhN@sy|Q%Ktv!w=gNS~9RRB8W9Je8 zfB-N60Pri<9>AgWw8R=0l>x83&&%{2mF z&Hz-z|CA?&UZwb$4fZ-zrRvL#o;X4dowpqy)iQ$xRxKH=8$1mSqULGV{-jjG*Yya=%rTPi8**gP6EU zm&8vfyK5yN=$NK46>y%+V6`?r_<;DyqD{&Dpt_8qAa~445Z^(Fv_q(JwMovNeIkSV z+Jc(I`gw#ntxPJ61#w@{;3Jbzm*CeI`e{#+9KUJn)X$)lSQ(MvH-}3gaAw!*TUGCT z--tgT2{KK^yT!eq^bQ*0uZYYjV)#^;wJj-_$F;^8*3h%k^xBWr>t^YW0j90x=fHh# zcrkaX0{fUBB}3E9Azr`?7{~cbla;zw6X_o9R{8Mtw8RIfJ;WGQEcm1)6%@pd^BJb3 zj*Tg$Rw?z0kHEgukJkfdjmReBFPs%ZBzB=nIcf-Sd-d z_Bd#DGQHfa+N>mVFo2IfxCBaOL@CKuF=f=SX6L^8*L6F4e)=v`r$VpO203`&g7ubt z#mv$xYpYQOhL(y>@+;(@WAeX#=?3@x_0E%Rx z5!#N>zI5;YC4ikRzx$@cgQrcmd^B5kadP;OpYlI!5dXtQ%Wz+349tmqwSIK!wj}PO z!0HfFf1TKfzJ6FXE_2d(lu?4KX`?E1umn=Pe;C3iS1D2_Bv+NZUfJ+FPb=hGZMeBk zyg=owuB*x9&*-e2CRo&@o{NwwxK1amo8N`sT(Yu#NtiHppvn9(Yf=<)#P~TmrPnhvY2R!liT%yq2 zFPS1+K@?#FS*0zlXE%(+y~HlMUaXHty01v0dJ{H}%L6tmPI(kG=a!~VV}1x;zY{vo zRpM2kYLU|NJfp66W2EEKdD(nj$au zQVH8SA(jfIx%@`8R_@)SGZR7*PzIO*8+Iz zc3Jz}D1L8#^(C!xF~Kw`e!9`$eFy^=;wi$kKb^*Ey~9=LA;+B~@+CB|Rsp3)gfocL zlQz|%6#ySEh=7 zCxUGx0}(adf#z*=n6VvARNYU}k@i*;=7*E&=0tE1WFp#EK%|Utz)t;{Q8tLl4yj&? z>Tph3c#HN~bXxVCijtM6emGnEY3sXS%>ctM6`GlY?$M(d>jM`=KGzQJ)>~7r-2pWg z54%ns!zw#ci=*0L-^1{ifQcS< z&FxoJ<;MM^*4M$cbs`=fUj)2mU%{ORuK_@{nn$A(lDJ>$B1d1)LBBnrq%U>sY5L~L z;&-o0Any|3xp2q~Ho0CnBxWVAE?o+u=-YPTL>9{v-lc}&nb;mY=AD+RscDKpB8uXh zc6LSMCpLJ#^tgndguY_^z!hqHyZqRyAian!7}22mw4u@=*EG|2E@g)*Yt+_%zN>r*kj;JZ z533hYqHia3={x@PUU6|4x|84Cw1eq0K7WIY_EE3fh&~%ySt8OrBW~ZF@{qOPhH0_f z#h8W`y8Xhl0&gVidzatg_z`f{HC|BKM5ScWcv4AP`)$Xf_{rzR7(**To!WCJ!lou)Tv+$>Pa|F zRDhA`S-h8Jr;R%19j$!-2Xkn1%^~nf;NC-A&{yvZj>lqFMUv0vGfCnft&$jiZ9uF_ z_&?p!10|y`Hr3TuMXJeqKFv?fo8+e1=7x;$+eGu}_uU5Rg z_Z#npcrBt>YGUme;%l3_f3b|SeD`h>Vys26>R;_ zllp6SqEC6H6u9jvv_6#n7Pb=HD$|LsY*esaqY**YhTEDJRpB`tAhOu@$7vKrQm4XJ zwbtmu=Jkx#b9Bb*VD3jXD1wDAqP3*^AZzE&7@YhslR#ZwAsJ!3N%p0o_{8lz3F}EO zx>qb8aeMKwe+BNJ1-q#WAzK4g-TCMH>LXRi?;uH{FWk5F z_@(;|(3)?TqDG@5{#yI=u%T??89xKk!Y(jsCgq52Y`aIbV z2kEzNMwQQPV%E-rg^bOLs!bllXtnM;EPQmnF>`7{yc$32p`&iqBm6dp311uL|2EoBtPJTIZ_~|6=rPZ{J3#I z{z13lzd{MRqKz1?AumbIUC}K#9kEMutNpIy;QF%vyFnEOf^GEIUp0S$ZdUv}y)f(vP{UPiuRu7eWRnzJRj5bivu55l8op#hc9{n-iSQs6AN1C>A>>hs38;||Y zV;K_YzHXoQ-5g&}JXiPC+x3}&=T>M4fzrGZ#b0-J;g6%f{E^;;JNe`_ z33-&G3OKRk`vyENPB&&Cjh8^|)V$lX_)X<0E3sx3BSIeLPIvKun-KcD0DQcm*;zhO zcf9`Q6he(IkhT`Foz-~)etBTp8Z7S^BWsR(x$}NT?rSw^s^aPW(wRN=pyi?}HKv~P zFU7dIA9ahR3xCKADZq;1RIa=y`hGd`Mnw*FkvLa7V)fkC?HQ*z9d`BO+{I)+*=S{R z%}ibKmvDn;bJllLZFBsqP&6HncDRiVRKV7l)!8zW2DZIMDYh7)XP3ZA`3tB^Bk7sn zy;1~6?yjV{;&h3ETJ!bw1{sa{JNFMWa_4v*B<$rD1XE9MS`O}ut}NBbQF-9v4Z|KX zWgF$wJ?-`VbE3maKu!V^G@T#FYI_?kZ@!OTrSM6 zD3u#Fc7CogcO&L50nVh9-s61OGri(Qj1!Z(qe4-B`bozdP zcLE|C8+5u_GlYuLF$Z_7AiAphShVDW!-gPZFRu?XYs|}8chKP!&8jTN^zG`KdG-QA zrVxHei`;d@$h|o-RhfO|in12yoD74ZgZ|SV|EVXF?4nBsR8Ca#MY{LU2i;t*zr+_h z8~0dg>eU+BkIIjYeL2O06}OVaBX|{j#T``VMmnV~for6sw8vDv8eEwqf?@9Yo@egJ z>&zKh1uR{qMh53{oVPl4n-10U3F&WJ2WP&mg(JH_@h8UFV2cBb6+?#z#E9$;>QNhA zm|pQ8p)L_-l6ftEE@xc@*asGtOJEco`ygJ@1}j>Gk?MrF!|0Stm7FY` zi^>U{m>}D7t*7z!EHXUmI1DJJ^u{c^8x!D<5vF{*1|+5w2s)N*NQ*a$n&~J_IcJ*P zNkg`5c5QcD4F^M|dI?L{oPUWDC0GU-Q*VaV^aNp5uC4twWfK_@;On|;OrT9Q($6z0 zF<`@b4a{)$tGaSCQk$|Wx%=pQ05MD%&|JT(KNZ`JxF-kSF4pDo^)Yc|`p@21B%{ih zHjTw(i3C}SPcQx!XYEDRY09h&`$pATt1(~uS(6-<8&ZkRc%)oP+Lmj3!zfszE5HGH z9U4p;D*R_QnE50ks*PG~ZF!#I!;5m=?gX>peUdE_J2eVwB3fr5P7I}EUvXadxT=Zj zm@ejFvkr3O5|~}i0h6Hp3eT7k5v5!Mr7J$QWcwpZW75+40;UH;h^P0{!O#zZoK3gSP*wM!bL}-?nGn%0boJ;j90|257Uv$ueKJ z4*p`+&cZ>ZgRx4P-R6+JmwrD^`AT}6a``>#)wR?wN;y;6AQkD2H6a!8s^$`&r8J&V z!v=2mBGquC9A1+qKg2rZ#Aqnp4aL@oJh5A9X-|YN0SIybSN*UwTZ*XkHdSz6oDZCW zGe%f|Nc(#@hhf1&E+2|%*+r=|AIQ;f3Q02*uU1NTaeT%c>FQk93Z-`=lnkpl9vblQ z=8{(HHLBhZ?=a?*X?16q%$3y74KZL;bYxApJIXntThE}&-P?;kd1(*gI6YJxQRJIf zygKx)G}TW7aS_AQN+;-j^6+n!H`T1~W`Q)j1jS4sO(~#S>24dC+)^0%eMFAW@0r)Q zZeA`I&Z`HN({OVlDoK&)tc$kH+-3r8aZ^o0sW&7)QAYBi;)6E7Py{E~G#(!MRope_ x`#X3#ek+Gj?K$V(8gxcZT2$2{19E(OaZaILJ)V)T9$(-8f@vb!I_7fve*ocNLqz}p literal 0 HcmV?d00001 diff --git a/frontend/src/modules/3DViewerNew/preview.tsx b/frontend/src/modules/3DViewerNew/preview.tsx index 2a9746ab7..831e956e2 100644 --- a/frontend/src/modules/3DViewerNew/preview.tsx +++ b/frontend/src/modules/3DViewerNew/preview.tsx @@ -1,8 +1,6 @@ import { DrawPreviewFunc } from "@framework/Preview"; -import previewImg from "./preview.webp"; +import previewImg from "./preview.jpg"; export const preview: DrawPreviewFunc = function (width: number, height: number) { - return ( - - ); + return ; }; diff --git a/frontend/src/modules/3DViewerNew/preview.webp b/frontend/src/modules/3DViewerNew/preview.webp deleted file mode 100644 index d180acb38d4be2486e7125d9adddd4b771a921cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52254 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 Td5qy!>Iqlnl?KhTd;kCd!im;n diff --git a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx b/frontend/src/modules/3DViewerNew/settings/components/dataProviderManagerComponentWrapper.tsx similarity index 93% rename from frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx rename to frontend/src/modules/3DViewerNew/settings/components/dataProviderManagerComponentWrapper.tsx index 5d1499fbf..5cf3aaec3 100644 --- a/frontend/src/modules/3DViewerNew/settings/components/layerManagerComponentWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/settings/components/dataProviderManagerComponentWrapper.tsx @@ -183,10 +183,45 @@ export function LayerManagerComponentWrapper(props: LayerManagerComponentWrapper return true; } - const hasView = - groupDelegate.getDescendantItems((item) => item instanceof Group && item.getGroupType() === GroupType.VIEW) - .length > 0; - const adjustedLayerActions = hasView ? LAYER_ACTIONS : INITIAL_LAYER_ACTIONS; + function makeActionsForGroup(group: ItemGroup): ActionGroup[] { + const hasView = + groupDelegate.getDescendantItems((item) => item instanceof Group && item.getGroupType() === GroupType.VIEW) + .length > 0; + + const hasViewAncestor = + group + .getGroupDelegate() + .getAncestors((item) => item instanceof Group && item.getGroupType() === GroupType.VIEW).length > 0; + const actions: ActionGroup[] = []; + + if (!hasView) { + return INITIAL_ACTIONS; + } + + const groupActions: ActionGroup = { + label: "Groups", + children: [], + }; + + if (!hasViewAncestor) { + groupActions.children.push({ + identifier: "view", + icon: , + label: "View", + }); + } + + groupActions.children.push({ + identifier: "settings-group", + icon: , + label: "Settings group", + }); + + actions.push(groupActions); + actions.push(...ACTIONS); + + return actions; + } return ( } - groupActions={adjustedLayerActions} + groupActions={makeActionsForGroup} onAction={handleLayerAction} isMoveAllowed={checkIfItemMoveAllowed} /> @@ -238,7 +273,7 @@ function ViewLayoutMenuItem(props: ViewLayoutMenuItemProps): React.ReactNode { ); } -const INITIAL_LAYER_ACTIONS: ActionGroup[] = [ +const INITIAL_ACTIONS: ActionGroup[] = [ { label: "Groups", children: [ @@ -256,22 +291,7 @@ const INITIAL_LAYER_ACTIONS: ActionGroup[] = [ }, ]; -const LAYER_ACTIONS: ActionGroup[] = [ - { - label: "Groups", - children: [ - { - identifier: "view", - icon: , - label: "View", - }, - { - identifier: "settings-group", - icon: , - label: "Settings group", - }, - ], - }, +const ACTIONS: ActionGroup[] = [ { label: "Layers", children: [ diff --git a/frontend/src/modules/3DViewerNew/settings/settings.tsx b/frontend/src/modules/3DViewerNew/settings/settings.tsx index ee9854e0c..480497e8f 100644 --- a/frontend/src/modules/3DViewerNew/settings/settings.tsx +++ b/frontend/src/modules/3DViewerNew/settings/settings.tsx @@ -10,7 +10,7 @@ import { useAtom, useAtomValue, useSetAtom } from "jotai"; import { providerManagerAtom, preferredViewLayoutAtom, userSelectedFieldIdentifierAtom } from "./atoms/baseAtoms"; import { selectedFieldIdentifierAtom } from "./atoms/derivedAtoms"; -import { LayerManagerComponentWrapper } from "./components/layerManagerComponentWrapper"; +import { LayerManagerComponentWrapper } from "./components/dataProviderManagerComponentWrapper"; import { DataProviderManager, DataProviderManagerTopic, diff --git a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx index 8fcade128..a5c73b4f7 100644 --- a/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/3DViewerNew/view/components/LayersWrapper.tsx @@ -174,6 +174,7 @@ export function LayersWrapper(props: LayersWrapperProps): React.ReactNode { id: item.id, name: item.name, isSync: true, + show3D: true, layerIds, }); From 198e83a0e707e0cd15759e4b102be1f9d13ec1de Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 13 May 2025 13:07:02 +0200 Subject: [PATCH 96/97] wip --- .../registerAllDataProviders.ts | 2 +- frontend/src/modules/3DViewerNew/loadModule.tsx | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/registerAllDataProviders.ts b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/registerAllDataProviders.ts index ee7d3be41..117d54bb7 100644 --- a/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/registerAllDataProviders.ts +++ b/frontend/src/modules/3DViewerNew/DataProviderFramework/customDataProviderImplementations/registerAllDataProviders.ts @@ -2,8 +2,8 @@ import { DataProviderRegistry } from "@modules/_shared/DataProviderFramework/dat import { CustomDataProviderType } from "./dataProviderTypes"; import { RealizationSeismicCrosslineProvider } from "./RealizationSeismicCrosslineProvider"; -import { RealizationSeismicInlineProvider } from "./RealizationSeismicInlineProvider"; import { RealizationSeismicDepthSliceProvider } from "./RealizationSeismicDepthProvider"; +import { RealizationSeismicInlineProvider } from "./RealizationSeismicInlineProvider"; DataProviderRegistry.registerDataProvider( CustomDataProviderType.REALIZATION_SEISMIC_CROSSLINE, diff --git a/frontend/src/modules/3DViewerNew/loadModule.tsx b/frontend/src/modules/3DViewerNew/loadModule.tsx index dfcfa27de..8bb478e21 100644 --- a/frontend/src/modules/3DViewerNew/loadModule.tsx +++ b/frontend/src/modules/3DViewerNew/loadModule.tsx @@ -1,10 +1,13 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; +import type { Interfaces } from "./interfaces"; +import { settingsToViewInterfaceInitialization } from "./interfaces"; import { MODULE_NAME } from "./registerModule"; import { Settings } from "./settings/settings"; import { View } from "./view/view"; +import "./DataProviderFramework/customDataProviderImplementations/registerAllDataProviders"; + const module = ModuleRegistry.initModule(MODULE_NAME, { settingsToViewInterfaceInitialization, }); From bd67340f84524ccd571c56e1b9cb27fe2b2c3084 Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 13 May 2025 17:14:37 +0200 Subject: [PATCH 97/97] wip --- frontend/package-lock.json | 2368 +++++++++++++---- frontend/package.json | 2 +- .../view/components/LayersWrapper.tsx | 5 +- 3 files changed, 1798 insertions(+), 577 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 463461eef..9f9c081d4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -16,8 +16,6 @@ "@deck.gl/layers": "^9.0.33", "@deck.gl/mesh-layers": "^9.0.33", "@deck.gl/react": "^9.0.33", - "@equinor/eds-core-react": "^0.42.5", - "@equinor/esv-intersection": "^3.0.10", "@equinor/eds-core-react": "^0.45.1", "@equinor/esv-intersection": "^3.1.2", "@headlessui/react": "^1.7.8", @@ -46,9 +44,9 @@ "react-plotly.js": "^2.6.0", "simplify-js": "^1.2.4", "uuid": "^9.0.0", - "workerpool": "^9.2.0" "vite-plugin-node-polyfills": "^0.23.0", - "wonka": "^6.3.4" + "wonka": "^6.3.4", + "workerpool": "^9.2.0" }, "devDependencies": { "@hey-api/openapi-ts": "^0.61.1", @@ -112,6 +110,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", @@ -126,6 +125,7 @@ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -135,6 +135,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -160,17 +161,12 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/generator": { + "node_modules/@babel/core/node_modules/@babel/generator": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "dev": true, + "license": "MIT", "dependencies": { "@babel/parser": "^7.27.1", "@babel/types": "^7.27.1", @@ -182,11 +178,61 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/core/node_modules/@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/core/node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@babel/helper-compilation-targets": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", @@ -202,6 +248,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" @@ -210,11 +257,67 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-module-imports/node_modules/@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports/node_modules/@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-module-imports/node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@babel/helper-module-transforms": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", @@ -227,6 +330,65 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@babel/helper-plugin-utils": { "version": "7.26.5", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", @@ -241,6 +403,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -249,6 +412,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -258,6 +422,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -267,6 +432,7 @@ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.27.1", "@babel/types": "^7.27.1" @@ -279,6 +445,7 @@ "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "license": "MIT", "dependencies": { "@babel/types": "^7.27.1" }, @@ -325,6 +492,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -333,6 +501,7 @@ "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", @@ -342,35 +511,11 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", - "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" @@ -390,70 +535,11 @@ "findup": "bin/findup.js" } }, - "node_modules/@deck.gl-community/editable-layers": { - "version": "9.1.0-beta.5", - "resolved": "https://registry.npmjs.org/@deck.gl-community/editable-layers/-/editable-layers-9.1.0-beta.5.tgz", - "integrity": "sha512-kMlGepC81fRPE4Vuu9bsF4dv0Jz+5ZIfYRmfedWNyC1S9zJAFBBkIwkfFPEMC8IkL4Tjd1CRfCVyOcy3CqBd+w==", - "dependencies": { - "@turf/along": "^6.5.0", - "@turf/area": "^6.5.0", - "@turf/bbox": "^6.5.0", - "@turf/bbox-polygon": "^6.5.0", - "@turf/bearing": "^6.5.0", - "@turf/boolean-point-in-polygon": "^6.5.0", - "@turf/buffer": "^6.5.0", - "@turf/center": "^6.5.0", - "@turf/centroid": "^6.5.0", - "@turf/circle": "^6.5.0", - "@turf/clone": "^6.5.0", - "@turf/destination": "^6.5.0", - "@turf/difference": "^6.5.0", - "@turf/distance": "^6.5.0", - "@turf/ellipse": "^6.5.0", - "@turf/helpers": "^6.5.0", - "@turf/intersect": "^6.5.0", - "@turf/invariant": "^6.5.0", - "@turf/line-intersect": "^6.5.0", - "@turf/meta": "^6.5.0", - "@turf/midpoint": "^6.5.0", - "@turf/nearest-point-on-line": "^6.5.0", - "@turf/point-to-line-distance": "^6.5.0", - "@turf/polygon-to-line": "^6.5.0", - "@turf/rewind": "^6.5.0", - "@turf/transform-rotate": "^6.5.0", - "@turf/transform-scale": "^6.5.0", - "@turf/transform-translate": "^6.5.0", - "@turf/union": "^6.5.0", - "@types/geojson": "^7946.0.14", - "cubic-hermite-spline": "^1.0.1", - "eventemitter3": "^5.0.0", - "lodash.omit": "^4.1.1", - "lodash.throttle": "^4.1.1", - "uuid": "9.0.0", - "viewport-mercator-project": ">=6.2.3" - }, - "peerDependencies": { - "@deck.gl-community/layers": "^9.1.0-beta.2", - "@deck.gl/core": "^9.1.0", - "@deck.gl/extensions": "^9.1.0", - "@deck.gl/geo-layers": "^9.1.0", - "@deck.gl/layers": "^9.1.0", - "@deck.gl/mesh-layers": "^9.1.0", - "@luma.gl/constants": ">=9.1.0", - "@luma.gl/core": ">=9.1.0", - "@luma.gl/engine": ">=9.1.0", - "@math.gl/core": ">=4.0.1" - } - }, - "node_modules/@deck.gl-community/editable-layers/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" - }, "node_modules/@deck.gl-community/layers": { "version": "9.1.0-beta.5", "resolved": "https://registry.npmjs.org/@deck.gl-community/layers/-/layers-9.1.0-beta.5.tgz", "integrity": "sha512-1aYrRBCzwYG4K7thxLvKlBIiDFxJPr8UElzqLVDPSdAsp6TDrlQYp/1NmHfzVu1KQeRg+ovcKalkB9gDXuCqOw==", + "license": "MIT", "peer": true, "dependencies": { "@deck.gl/core": "^9.1.0", @@ -473,28 +559,189 @@ "@math.gl/core": "^4.0.0" } }, - "node_modules/@deck.gl/aggregation-layers": { + "node_modules/@deck.gl-community/layers/node_modules/@deck.gl/extensions": { "version": "9.1.11", - "resolved": "https://registry.npmjs.org/@deck.gl/aggregation-layers/-/aggregation-layers-9.1.11.tgz", - "integrity": "sha512-3sVHcNsLj8Lzevf/2uZUn4pv6Z5Bpz7CzwataQ0EWGjjzHJ/4uIi24EIYEJmTzLGueenFS+70s8hWujJaAJMoQ==", + "resolved": "https://registry.npmjs.org/@deck.gl/extensions/-/extensions-9.1.11.tgz", + "integrity": "sha512-wnWUhd7yyQTdHB0x2fE/FPeZUz5ZzKNDx2FwY+bQuQVaiNcc5YpGKQSV0ahIKUg6NF+shxZIJ/WBVi3wc6RFRQ==", + "license": "MIT", + "peer": true, "dependencies": { "@luma.gl/constants": "^9.1.5", "@luma.gl/shadertools": "^9.1.5", + "@math.gl/core": "^4.1.0" + }, + "peerDependencies": { + "@deck.gl/core": "^9.1.0", + "@luma.gl/core": "^9.1.5", + "@luma.gl/engine": "^9.1.5" + } + }, + "node_modules/@deck.gl-community/layers/node_modules/@deck.gl/geo-layers": { + "version": "9.1.11", + "resolved": "https://registry.npmjs.org/@deck.gl/geo-layers/-/geo-layers-9.1.11.tgz", + "integrity": "sha512-z2rYT9617JaIP+a1nKhbxu32a5BjiR5L0Tmd4x7UUOlipx6A5850SyPyIUx5ca7lpIlbukGRjg600pRKKk3XEA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@loaders.gl/3d-tiles": "^4.2.0", + "@loaders.gl/gis": "^4.2.0", + "@loaders.gl/loader-utils": "^4.2.0", + "@loaders.gl/mvt": "^4.2.0", + "@loaders.gl/schema": "^4.2.0", + "@loaders.gl/terrain": "^4.2.0", + "@loaders.gl/tiles": "^4.2.0", + "@loaders.gl/wms": "^4.2.0", + "@luma.gl/gltf": "^9.1.5", + "@luma.gl/shadertools": "^9.1.5", "@math.gl/core": "^4.1.0", + "@math.gl/culling": "^4.1.0", "@math.gl/web-mercator": "^4.1.0", - "d3-hexbin": "^0.2.1" + "@types/geojson": "^7946.0.8", + "h3-js": "^4.1.0", + "long": "^3.2.0" }, "peerDependencies": { "@deck.gl/core": "^9.1.0", + "@deck.gl/extensions": "^9.1.0", "@deck.gl/layers": "^9.1.0", + "@deck.gl/mesh-layers": "^9.1.0", + "@loaders.gl/core": "^4.2.0", + "@luma.gl/core": "^9.1.5", + "@luma.gl/engine": "^9.1.5" + } + }, + "node_modules/@deck.gl-community/layers/node_modules/@deck.gl/layers": { + "version": "9.1.11", + "resolved": "https://registry.npmjs.org/@deck.gl/layers/-/layers-9.1.11.tgz", + "integrity": "sha512-RDFF+YH4BP3HPUCtblnfTaWXBt1DBk0i7FbQ9jgcjdv4CB7MtEOHme5k2LJ0cBV4ROQtSHg/vjAvMayZewI4vg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@loaders.gl/images": "^4.2.0", + "@loaders.gl/schema": "^4.2.0", + "@luma.gl/shadertools": "^9.1.5", + "@mapbox/tiny-sdf": "^2.0.5", + "@math.gl/core": "^4.1.0", + "@math.gl/polygon": "^4.1.0", + "@math.gl/web-mercator": "^4.1.0", + "earcut": "^2.2.4" + }, + "peerDependencies": { + "@deck.gl/core": "^9.1.0", + "@loaders.gl/core": "^4.2.0", "@luma.gl/core": "^9.1.5", "@luma.gl/engine": "^9.1.5" } }, + "node_modules/@deck.gl-community/layers/node_modules/@deck.gl/mesh-layers": { + "version": "9.1.11", + "resolved": "https://registry.npmjs.org/@deck.gl/mesh-layers/-/mesh-layers-9.1.11.tgz", + "integrity": "sha512-9zdtRV8rVyJI4t8bPE3/jLEuiBfCp+53KneFrh/nmANVupZ5SP44TobSWJWl+nZ3Xs4LkJXaQPbI8gZR39lJBQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@loaders.gl/gltf": "^4.2.0", + "@luma.gl/gltf": "^9.1.5", + "@luma.gl/shadertools": "^9.1.5" + }, + "peerDependencies": { + "@deck.gl/core": "^9.1.0", + "@luma.gl/core": "^9.1.5", + "@luma.gl/engine": "^9.1.5" + } + }, + "node_modules/@deck.gl-community/layers/node_modules/@luma.gl/constants": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/constants/-/constants-9.1.9.tgz", + "integrity": "sha512-yc9fml04OeTTcwK+7gmDMxoLQ67j4ZiAFXjmYvPomYyBVzS0NZxTDuwcCBmnxjLOiroOZW8FRRrVc/yOiFug2w==", + "license": "MIT", + "peer": true + }, + "node_modules/@deck.gl-community/layers/node_modules/@luma.gl/core": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/core/-/core-9.1.9.tgz", + "integrity": "sha512-1i9N7+I/UbFjx3axSMlc3/NufA+C2iBv/7mw51gRE/ypQPgvFmY/QqXBVZRe+nthF+OhlUMhO19TBndzYFTWhA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@math.gl/types": "^4.1.0", + "@probe.gl/env": "^4.0.8", + "@probe.gl/log": "^4.0.8", + "@probe.gl/stats": "^4.0.8", + "@types/offscreencanvas": "^2019.6.4" + } + }, + "node_modules/@deck.gl-community/layers/node_modules/@luma.gl/engine": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/engine/-/engine-9.1.9.tgz", + "integrity": "sha512-n1GLK1sUMFkWxdb+aZYn6ZBFltFEMi7X+6ZPxn2pBsNT6oeF4AyvH5AyqhOpvHvUnCLDt3Zsf1UIfx3MI//YSw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@math.gl/core": "^4.1.0", + "@math.gl/types": "^4.1.0", + "@probe.gl/log": "^4.0.8", + "@probe.gl/stats": "^4.0.8" + }, + "peerDependencies": { + "@luma.gl/core": "^9.1.0", + "@luma.gl/shadertools": "^9.1.0" + } + }, + "node_modules/@deck.gl-community/layers/node_modules/@luma.gl/gltf": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/gltf/-/gltf-9.1.9.tgz", + "integrity": "sha512-KgVBIFCtRO1oadgMDycMJA5s+q519l/fQBGAZpUcLfWsaEDQfdHW2NLdrK/00VDv46Ng8tN/O6uyH6E40uLcLw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@loaders.gl/core": "^4.2.0", + "@loaders.gl/textures": "^4.2.0", + "@math.gl/core": "^4.1.0" + }, + "peerDependencies": { + "@luma.gl/core": "^9.1.0", + "@luma.gl/engine": "^9.1.0", + "@luma.gl/shadertools": "^9.1.0" + } + }, + "node_modules/@deck.gl-community/layers/node_modules/@luma.gl/shadertools": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/shadertools/-/shadertools-9.1.9.tgz", + "integrity": "sha512-Uqp2xfgIEunRMLXTeCJ4uEMlWcUGcYMZGJ8GAOrAeDzn4bMKVRKmZDC71vkuTctnaodM3UdrI9W6s1sJlrXsxw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@math.gl/core": "^4.1.0", + "@math.gl/types": "^4.1.0", + "wgsl_reflect": "^1.2.0" + }, + "peerDependencies": { + "@luma.gl/core": "^9.1.0" + } + }, + "node_modules/@deck.gl/aggregation-layers": { + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/aggregation-layers/-/aggregation-layers-9.0.40.tgz", + "integrity": "sha512-tZ3NEDVlZnCnwbxdoB+qB184gSricnbcOZwkPHqNWk+2wadyd6Q0j9V9aZ9V6M5BGDn86DrOKPFygwmUl/jkwA==", + "dependencies": { + "@luma.gl/constants": "~9.0.27", + "@luma.gl/shadertools": "~9.0.27", + "@math.gl/web-mercator": "^4.0.0", + "d3-hexbin": "^0.2.1" + }, + "peerDependencies": { + "@deck.gl/core": "^9.0.0", + "@deck.gl/layers": "^9.0.0", + "@luma.gl/core": "~9.0.0", + "@luma.gl/engine": "~9.0.0" + } + }, "node_modules/@deck.gl/core": { "version": "9.1.11", "resolved": "https://registry.npmjs.org/@deck.gl/core/-/core-9.1.11.tgz", "integrity": "sha512-ibqeLgx7fRqvJeCWtugbPE/LcOl3uDy8wSSd+l0FBhOqOPQcLdluTVMs0JW2OyqMuR2eOqcfarxzXdDVM7CcJA==", + "license": "MIT", "dependencies": { "@loaders.gl/core": "^4.2.0", "@loaders.gl/images": "^4.2.0", @@ -515,25 +762,88 @@ "mjolnir.js": "^3.0.0" } }, + "node_modules/@deck.gl/core/node_modules/@luma.gl/constants": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/constants/-/constants-9.1.9.tgz", + "integrity": "sha512-yc9fml04OeTTcwK+7gmDMxoLQ67j4ZiAFXjmYvPomYyBVzS0NZxTDuwcCBmnxjLOiroOZW8FRRrVc/yOiFug2w==", + "license": "MIT" + }, + "node_modules/@deck.gl/core/node_modules/@luma.gl/core": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/core/-/core-9.1.9.tgz", + "integrity": "sha512-1i9N7+I/UbFjx3axSMlc3/NufA+C2iBv/7mw51gRE/ypQPgvFmY/QqXBVZRe+nthF+OhlUMhO19TBndzYFTWhA==", + "license": "MIT", + "dependencies": { + "@math.gl/types": "^4.1.0", + "@probe.gl/env": "^4.0.8", + "@probe.gl/log": "^4.0.8", + "@probe.gl/stats": "^4.0.8", + "@types/offscreencanvas": "^2019.6.4" + } + }, + "node_modules/@deck.gl/core/node_modules/@luma.gl/engine": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/engine/-/engine-9.1.9.tgz", + "integrity": "sha512-n1GLK1sUMFkWxdb+aZYn6ZBFltFEMi7X+6ZPxn2pBsNT6oeF4AyvH5AyqhOpvHvUnCLDt3Zsf1UIfx3MI//YSw==", + "license": "MIT", + "dependencies": { + "@math.gl/core": "^4.1.0", + "@math.gl/types": "^4.1.0", + "@probe.gl/log": "^4.0.8", + "@probe.gl/stats": "^4.0.8" + }, + "peerDependencies": { + "@luma.gl/core": "^9.1.0", + "@luma.gl/shadertools": "^9.1.0" + } + }, + "node_modules/@deck.gl/core/node_modules/@luma.gl/shadertools": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/shadertools/-/shadertools-9.1.9.tgz", + "integrity": "sha512-Uqp2xfgIEunRMLXTeCJ4uEMlWcUGcYMZGJ8GAOrAeDzn4bMKVRKmZDC71vkuTctnaodM3UdrI9W6s1sJlrXsxw==", + "license": "MIT", + "dependencies": { + "@math.gl/core": "^4.1.0", + "@math.gl/types": "^4.1.0", + "wgsl_reflect": "^1.2.0" + }, + "peerDependencies": { + "@luma.gl/core": "^9.1.0" + } + }, + "node_modules/@deck.gl/core/node_modules/@luma.gl/webgl": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/webgl/-/webgl-9.1.9.tgz", + "integrity": "sha512-jecHjhNSWkXH0v62rM6G5fIIkOmsrND27099iKgdutFvHIvd4QS4UzGWEEa9AEPlP0rTLqXkA6y6YL7f42ZkVg==", + "license": "MIT", + "dependencies": { + "@luma.gl/constants": "9.1.9", + "@math.gl/types": "^4.1.0", + "@probe.gl/env": "^4.0.8" + }, + "peerDependencies": { + "@luma.gl/core": "^9.1.0" + } + }, "node_modules/@deck.gl/extensions": { - "version": "9.1.11", - "resolved": "https://registry.npmjs.org/@deck.gl/extensions/-/extensions-9.1.11.tgz", - "integrity": "sha512-wnWUhd7yyQTdHB0x2fE/FPeZUz5ZzKNDx2FwY+bQuQVaiNcc5YpGKQSV0ahIKUg6NF+shxZIJ/WBVi3wc6RFRQ==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/extensions/-/extensions-9.0.40.tgz", + "integrity": "sha512-1lESbg4NLkXxonO5f6aEX9a1DP4d8Vd+YLz9O4SwBJeZQZSeo4d3iJuJPL3MyA4thqdaA1Q9Vi8gtD9Mru6asg==", "dependencies": { - "@luma.gl/constants": "^9.1.5", - "@luma.gl/shadertools": "^9.1.5", - "@math.gl/core": "^4.1.0" + "@luma.gl/constants": "~9.0.27", + "@luma.gl/shadertools": "~9.0.27", + "@math.gl/core": "^4.0.0" }, "peerDependencies": { - "@deck.gl/core": "^9.1.0", - "@luma.gl/core": "^9.1.5", - "@luma.gl/engine": "^9.1.5" + "@deck.gl/core": "^9.0.0", + "@luma.gl/core": "~9.0.0", + "@luma.gl/engine": "~9.0.0" } }, "node_modules/@deck.gl/geo-layers": { - "version": "9.1.11", - "resolved": "https://registry.npmjs.org/@deck.gl/geo-layers/-/geo-layers-9.1.11.tgz", - "integrity": "sha512-z2rYT9617JaIP+a1nKhbxu32a5BjiR5L0Tmd4x7UUOlipx6A5850SyPyIUx5ca7lpIlbukGRjg600pRKKk3XEA==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/geo-layers/-/geo-layers-9.0.40.tgz", + "integrity": "sha512-kIxryoyWzicqLvAImtuUSX7NEntSdMIjCq0DoDdvSfhE37R0FxDRc5Vse5fjg9/1nNshw7mEcD2KxUS8MI8ypA==", "dependencies": { "@loaders.gl/3d-tiles": "^4.2.0", "@loaders.gl/gis": "^4.2.0", @@ -543,29 +853,30 @@ "@loaders.gl/terrain": "^4.2.0", "@loaders.gl/tiles": "^4.2.0", "@loaders.gl/wms": "^4.2.0", - "@luma.gl/gltf": "^9.1.5", - "@luma.gl/shadertools": "^9.1.5", - "@math.gl/core": "^4.1.0", - "@math.gl/culling": "^4.1.0", - "@math.gl/web-mercator": "^4.1.0", + "@luma.gl/gltf": "~9.0.27", + "@luma.gl/shadertools": "~9.0.27", + "@math.gl/core": "^4.0.0", + "@math.gl/culling": "^4.0.0", + "@math.gl/web-mercator": "^4.0.0", "@types/geojson": "^7946.0.8", "h3-js": "^4.1.0", "long": "^3.2.0" }, "peerDependencies": { - "@deck.gl/core": "^9.1.0", - "@deck.gl/extensions": "^9.1.0", - "@deck.gl/layers": "^9.1.0", - "@deck.gl/mesh-layers": "^9.1.0", + "@deck.gl/core": "^9.0.0", + "@deck.gl/extensions": "^9.0.0", + "@deck.gl/layers": "^9.0.0", + "@deck.gl/mesh-layers": "^9.0.0", "@loaders.gl/core": "^4.2.0", - "@luma.gl/core": "^9.1.5", - "@luma.gl/engine": "^9.1.5" + "@luma.gl/core": "~9.0.0", + "@luma.gl/engine": "~9.0.0" } }, "node_modules/@deck.gl/json": { "version": "9.1.11", "resolved": "https://registry.npmjs.org/@deck.gl/json/-/json-9.1.11.tgz", "integrity": "sha512-rSV5AqWqmt0/C9rgldk21Pbsfqk+OrrttG42VZBjCYW3RkosSOr0aUhSlAx3/tERXUNZoN46WBtzQWV1GQptxg==", + "license": "MIT", "dependencies": { "jsep": "^0.3.0" }, @@ -574,45 +885,45 @@ } }, "node_modules/@deck.gl/layers": { - "version": "9.1.11", - "resolved": "https://registry.npmjs.org/@deck.gl/layers/-/layers-9.1.11.tgz", - "integrity": "sha512-RDFF+YH4BP3HPUCtblnfTaWXBt1DBk0i7FbQ9jgcjdv4CB7MtEOHme5k2LJ0cBV4ROQtSHg/vjAvMayZewI4vg==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/layers/-/layers-9.0.40.tgz", + "integrity": "sha512-7emTkPLVWeVuV5VPBTmHDJegkGH1i76GuvhHNXikper/Zwljf9QVUacPqk3or4lHBCzzlkeccCsRmTd3l+MBYQ==", "dependencies": { "@loaders.gl/images": "^4.2.0", "@loaders.gl/schema": "^4.2.0", - "@luma.gl/shadertools": "^9.1.5", "@mapbox/tiny-sdf": "^2.0.5", - "@math.gl/core": "^4.1.0", - "@math.gl/polygon": "^4.1.0", - "@math.gl/web-mercator": "^4.1.0", + "@math.gl/core": "^4.0.0", + "@math.gl/polygon": "^4.0.0", + "@math.gl/web-mercator": "^4.0.0", "earcut": "^2.2.4" }, "peerDependencies": { - "@deck.gl/core": "^9.1.0", + "@deck.gl/core": "^9.0.0", "@loaders.gl/core": "^4.2.0", - "@luma.gl/core": "^9.1.5", - "@luma.gl/engine": "^9.1.5" + "@luma.gl/core": "~9.0.0", + "@luma.gl/engine": "~9.0.0" } }, "node_modules/@deck.gl/mesh-layers": { - "version": "9.1.11", - "resolved": "https://registry.npmjs.org/@deck.gl/mesh-layers/-/mesh-layers-9.1.11.tgz", - "integrity": "sha512-9zdtRV8rVyJI4t8bPE3/jLEuiBfCp+53KneFrh/nmANVupZ5SP44TobSWJWl+nZ3Xs4LkJXaQPbI8gZR39lJBQ==", + "version": "9.0.40", + "resolved": "https://registry.npmjs.org/@deck.gl/mesh-layers/-/mesh-layers-9.0.40.tgz", + "integrity": "sha512-aCYnxfXmTrod+YMC6cHN4fP/OBihkZi+hgllJkBDiKkFr83cktJJGfjruHYYyQQuBKUPI1J2LP6aUD5/vXO2MA==", "dependencies": { "@loaders.gl/gltf": "^4.2.0", - "@luma.gl/gltf": "^9.1.5", - "@luma.gl/shadertools": "^9.1.5" + "@luma.gl/gltf": "~9.0.27", + "@luma.gl/shadertools": "~9.0.27" }, "peerDependencies": { - "@deck.gl/core": "^9.1.0", - "@luma.gl/core": "^9.1.5", - "@luma.gl/engine": "^9.1.5" + "@deck.gl/core": "^9.0.0", + "@luma.gl/core": "~9.0.0", + "@luma.gl/engine": "~9.0.0" } }, "node_modules/@deck.gl/react": { "version": "9.1.11", "resolved": "https://registry.npmjs.org/@deck.gl/react/-/react-9.1.11.tgz", "integrity": "sha512-RYjVYMZteyGwlZtuAiVD54TqvFgtZEJYBju2E/a6Bg3LBZ1eNtGHCpx5dIVKiOOU99gOkw2g2h0yrRgQt1j1cQ==", + "license": "MIT", "peerDependencies": { "@deck.gl/core": "^9.1.0", "@deck.gl/widgets": "^9.1.0", @@ -624,6 +935,7 @@ "version": "9.1.11", "resolved": "https://registry.npmjs.org/@deck.gl/widgets/-/widgets-9.1.11.tgz", "integrity": "sha512-E2t3YijvBEjIvPf2zTqaaPMYGpm3YLSTA3ap2JwEn+F+UQhkrCcahlPu4dXhpd0HHG8QEQCMZed/umad6kWZ2g==", + "license": "MIT", "peer": true, "dependencies": { "preact": "^10.17.0" @@ -636,6 +948,7 @@ "version": "0.4.92", "resolved": "https://registry.npmjs.org/@emerson-eps/color-tables/-/color-tables-0.4.92.tgz", "integrity": "sha512-qh/yKH6ue1qsQWP0gQxjIbNVMaI+eU9eoNxIWW15hX7bcFQgXGgVTk/IOF+9xQKx6UtU3Pd2yDcQon14j3BmoQ==", + "license": "MPL", "dependencies": { "d3": "^7.8.4", "d3-color": "^3.0.1", @@ -814,6 +1127,7 @@ "version": "0.45.1", "resolved": "https://registry.npmjs.org/@equinor/eds-core-react/-/eds-core-react-0.45.1.tgz", "integrity": "sha512-XMNjr7KLdUHULApRM3YoqBHVPoZVn5Z4rtVCGjAz2cwDcqMzv+R2cQ1flfBrb5ZD0/SUNpGMYOjSE1Z3RUyEaA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.27.0", "@equinor/eds-icons": "^0.22.0", @@ -838,12 +1152,14 @@ "node_modules/@equinor/eds-core-react/node_modules/@equinor/eds-icons": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/@equinor/eds-icons/-/eds-icons-0.22.0.tgz", - "integrity": "sha512-4SmPT67rUbl6qFyOCiSsue68EERgEGaXl9Xx6DkScW46AMbM6OdX3New1hctx1MIb6YdNlkHc/WwXTc+MG9j9Q==" + "integrity": "sha512-4SmPT67rUbl6qFyOCiSsue68EERgEGaXl9Xx6DkScW46AMbM6OdX3New1hctx1MIb6YdNlkHc/WwXTc+MG9j9Q==", + "license": "Apache-2.0" }, "node_modules/@equinor/eds-core-react/node_modules/@floating-ui/react": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.8.tgz", "integrity": "sha512-EQJ4Th328y2wyHR3KzOUOoTW2UKjFk53fmyahfwExnFQ8vnsMYqKc+fFPOkeYtj5tcp1DUMiNJ7BFhed7e9ONw==", + "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.1.2", "@floating-ui/utils": "^0.2.9", @@ -856,8 +1172,7 @@ }, "node_modules/@equinor/eds-icons": { "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@equinor/eds-icons/-/eds-icons-0.21.0.tgz", - "integrity": "sha512-k2keACHou9h9D5QLfSBeojTApqbPCkHNBWplUA/B9FQv8FMCMSBbjJAo2L/3yAExMylQN9LdwKo81T2tijRXoA==", + "license": "Apache-2.0", "engines": { "node": ">=10.0.0", "pnpm": ">=4" @@ -875,6 +1190,7 @@ "version": "0.8.7", "resolved": "https://registry.npmjs.org/@equinor/eds-utils/-/eds-utils-0.8.7.tgz", "integrity": "sha512-ra4yKMSr5/yseSYqEm7L9fGqR3b2IMaR9leOxlDs37FleOIPL1tI+PvVK+vYGluRmid/uD8X95N/pRQoWR21ow==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.10", "@equinor/eds-tokens": "0.9.2" @@ -889,6 +1205,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@equinor/esv-intersection/-/esv-intersection-3.1.2.tgz", "integrity": "sha512-rl0BXjLN0VaEzFfOq+bafJMTfP78mIttU2Bk2jBz/bU7tDg87SNK44nsFj5l8RlVv41qFG41/r2RpewcktJ6CA==", + "license": "MIT", "dependencies": { "@equinor/videx-math": "^1.1.0", "@equinor/videx-vector2": "^1.0.44", @@ -914,7 +1231,8 @@ "node_modules/@equinor/videx-math": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@equinor/videx-math/-/videx-math-1.1.1.tgz", - "integrity": "sha512-bEVXEzTkqHlUIgfHIQg6Ozp3eC0JKiqAWBTEbrWGnhEZBqpYpnZx9NYmZozoRFDfthlvHRcqSm77z8CIv3KuJQ==" + "integrity": "sha512-bEVXEzTkqHlUIgfHIQg6Ozp3eC0JKiqAWBTEbrWGnhEZBqpYpnZx9NYmZozoRFDfthlvHRcqSm77z8CIv3KuJQ==", + "license": "MIT" }, "node_modules/@equinor/videx-vector2": { "version": "1.0.44", @@ -927,6 +1245,7 @@ "version": "0.11.4", "resolved": "https://registry.npmjs.org/@equinor/videx-wellog/-/videx-wellog-0.11.4.tgz", "integrity": "sha512-hgOrX0EruiW7udrxBSMJiERi5II1tI0GxRxBuyVytVSY0BfmxCVhKL2WVgLJacInGvM8ELt6/BHcGi1Vf+6ivg==", + "license": "MIT", "dependencies": { "@equinor/videx-math": "^1.1.1", "d3-array": "^3.2.0", @@ -1128,9 +1447,8 @@ } }, "node_modules/@floating-ui/react": { - "version": "0.26.28", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", - "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "version": "0.26.24", + "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.1.2", "@floating-ui/utils": "^0.2.8", @@ -1155,12 +1473,14 @@ "node_modules/@floating-ui/utils": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", - "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==" + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", + "license": "MIT", "dependencies": { "@formatjs/fast-memoize": "2.2.7", "@formatjs/intl-localematcher": "0.6.1", @@ -1172,6 +1492,7 @@ "version": "2.2.7", "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", "dependencies": { "tslib": "^2.8.0" } @@ -1180,6 +1501,7 @@ "version": "2.11.2", "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", + "license": "MIT", "dependencies": { "@formatjs/ecma402-abstract": "2.3.4", "@formatjs/icu-skeleton-parser": "1.8.14", @@ -1190,6 +1512,7 @@ "version": "1.8.14", "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", + "license": "MIT", "dependencies": { "@formatjs/ecma402-abstract": "2.3.4", "tslib": "^2.8.0" @@ -1199,6 +1522,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", + "license": "MIT", "dependencies": { "tslib": "^2.8.0" } @@ -1376,6 +1700,7 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", + "license": "MIT", "peerDependencies": { "react": "*" } @@ -1384,6 +1709,7 @@ "version": "3.8.0", "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.0.tgz", "integrity": "sha512-J51AJ0fEL68hE4CwGPa6E0PO6JDaVLd8aln48xFCSy7CZkZc96dGEGmLs2OEEbBxcsVZtfrqkXJwI2/MSG8yKw==", + "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0" } @@ -1392,6 +1718,7 @@ "version": "3.1.7", "resolved": "https://registry.npmjs.org/@internationalized/message/-/message-3.1.7.tgz", "integrity": "sha512-gLQlhEW4iO7DEFPf/U7IrIdA3UyLGS0opeqouaFwlMObLUzwexRjbygONHDVbC9G9oFLXsLyGKYkJwqXw/QADg==", + "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0", "intl-messageformat": "^10.1.0" @@ -1401,6 +1728,7 @@ "version": "3.6.1", "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.1.tgz", "integrity": "sha512-UVsb4bCwbL944E0SX50CHFtWEeZ2uB5VozZ5yDXJdq6iPZsZO5p+bjVMZh2GxHf4Bs/7xtDCcPwEa2NU9DaG/g==", + "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0" } @@ -1409,6 +1737,7 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.2.6.tgz", "integrity": "sha512-LR2lnM4urJta5/wYJVV7m8qk5DrMZmLRTuFhbQO5b9/sKLHgty6unQy1Li4+Su2DWydmB4aZdS5uxBRXIq2aAw==", + "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0" } @@ -1531,38 +1860,187 @@ "license": "MIT" }, "node_modules/@loaders.gl/3d-tiles": { + "version": "4.2.5", + "license": "MIT", + "dependencies": { + "@loaders.gl/compression": "4.2.5", + "@loaders.gl/crypto": "4.2.5", + "@loaders.gl/draco": "4.2.5", + "@loaders.gl/gltf": "4.2.5", + "@loaders.gl/images": "4.2.5", + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/math": "4.2.5", + "@loaders.gl/tiles": "4.2.5", + "@loaders.gl/zip": "4.2.5", + "@math.gl/core": "^4.0.1", + "@math.gl/culling": "^4.0.1", + "@math.gl/geospatial": "^4.0.1", + "@probe.gl/log": "^4.0.4", + "long": "^5.2.1" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.0.0" + } + }, + "node_modules/@loaders.gl/3d-tiles/node_modules/long": { + "version": "5.2.3", + "license": "Apache-2.0" + }, + "node_modules/@loaders.gl/compression": { + "version": "4.2.5", + "license": "MIT", + "dependencies": { + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/worker-utils": "4.2.5", + "@types/brotli": "^1.3.0", + "@types/pako": "^1.0.1", + "fflate": "0.7.4", + "lzo-wasm": "^0.0.4", + "pako": "1.0.11", + "snappyjs": "^0.6.1" + }, + "optionalDependencies": { + "brotli": "^1.3.2", + "lz4js": "^0.2.0", + "zstd-codec": "^0.1" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.0.0" + } + }, + "node_modules/@loaders.gl/core": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@loaders.gl/core/-/core-4.3.3.tgz", + "integrity": "sha512-RaQ3uNg4ZaVqDRgvJ2CjaOjeeHdKvbKuzFFgbGnflVB9is5bu+h3EKc3Jke7NGVvLBsZ6oIXzkwHijVsMfxv8g==", + "license": "MIT", + "dependencies": { + "@loaders.gl/loader-utils": "4.3.3", + "@loaders.gl/schema": "4.3.3", + "@loaders.gl/worker-utils": "4.3.3", + "@probe.gl/log": "^4.0.2" + } + }, + "node_modules/@loaders.gl/core/node_modules/@loaders.gl/loader-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@loaders.gl/loader-utils/-/loader-utils-4.3.3.tgz", + "integrity": "sha512-8erUIwWLiIsZX36fFa/seZsfTsWlLk72Sibh/YZJrPAefuVucV4mGGzMBZ96LE2BUfJhadn250eio/59TUFbNw==", + "license": "MIT", + "dependencies": { + "@loaders.gl/schema": "4.3.3", + "@loaders.gl/worker-utils": "4.3.3", + "@probe.gl/log": "^4.0.2", + "@probe.gl/stats": "^4.0.2" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.3.0" + } + }, + "node_modules/@loaders.gl/core/node_modules/@loaders.gl/schema": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@loaders.gl/schema/-/schema-4.3.3.tgz", + "integrity": "sha512-zacc9/8je+VbuC6N/QRfiTjRd+BuxsYlddLX1u5/X/cg9s36WZZBlU1oNKUgTYe8eO6+qLyYx77yi+9JbbEehw==", + "license": "MIT", + "dependencies": { + "@types/geojson": "^7946.0.7" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.3.0" + } + }, + "node_modules/@loaders.gl/core/node_modules/@loaders.gl/worker-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@loaders.gl/worker-utils/-/worker-utils-4.3.3.tgz", + "integrity": "sha512-eg45Ux6xqsAfqPUqJkhmbFZh9qfmYuPfA+34VcLtfeXIwAngeP6o4SrTmm9LWLGUKiSh47anCEV1p7borDgvGQ==", + "license": "MIT", + "peerDependencies": { + "@loaders.gl/core": "^4.3.0" + } + }, + "node_modules/@loaders.gl/crypto": { + "version": "4.2.5", + "license": "MIT", + "dependencies": { + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/worker-utils": "4.2.5", + "@types/crypto-js": "^4.0.2" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.0.0" + } + }, + "node_modules/@loaders.gl/draco": { + "version": "4.2.5", + "license": "MIT", + "dependencies": { + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/schema": "4.2.5", + "@loaders.gl/worker-utils": "4.2.5", + "draco3d": "1.5.7" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.0.0" + } + }, + "node_modules/@loaders.gl/gis": { + "version": "4.2.5", + "license": "MIT", + "dependencies": { + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/schema": "4.2.5", + "@mapbox/vector-tile": "^1.3.1", + "@math.gl/polygon": "^4.0.0", + "pbf": "^3.2.1" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.0.0" + } + }, + "node_modules/@loaders.gl/gltf": { + "version": "4.2.5", + "license": "MIT", + "dependencies": { + "@loaders.gl/draco": "4.2.5", + "@loaders.gl/images": "4.2.5", + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/schema": "4.2.5", + "@loaders.gl/textures": "4.2.5", + "@math.gl/core": "^4.0.0" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.0.0" + } + }, + "node_modules/@loaders.gl/i3s": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/3d-tiles/-/3d-tiles-4.3.3.tgz", - "integrity": "sha512-3uOXE8W0ppbY7tI5ywrU3RwCLMZtd+Jh0KgY9+EbjBVnZDHcnFxytYuG4NzfJEf5zwv0jladeSbJS1oVbLi8Jw==", + "resolved": "https://registry.npmjs.org/@loaders.gl/i3s/-/i3s-4.3.3.tgz", + "integrity": "sha512-un4CoCxKNrTW4nhMTwQxRqPWA9/P2oG6+p5NJjnPPbEcs9QvrHJVXzCnzIpt2nvW7gyZyEHXfwBkVqGrZN48ng==", + "license": "MIT", + "peer": true, "dependencies": { "@loaders.gl/compression": "4.3.3", "@loaders.gl/crypto": "4.3.3", "@loaders.gl/draco": "4.3.3", - "@loaders.gl/gltf": "4.3.3", "@loaders.gl/images": "4.3.3", "@loaders.gl/loader-utils": "4.3.3", "@loaders.gl/math": "4.3.3", + "@loaders.gl/schema": "4.3.3", + "@loaders.gl/textures": "4.3.3", "@loaders.gl/tiles": "4.3.3", "@loaders.gl/zip": "4.3.3", "@math.gl/core": "^4.1.0", "@math.gl/culling": "^4.1.0", - "@math.gl/geospatial": "^4.1.0", - "@probe.gl/log": "^4.0.4", - "long": "^5.2.1" + "@math.gl/geospatial": "^4.1.0" }, "peerDependencies": { "@loaders.gl/core": "^4.3.0" } }, - "node_modules/@loaders.gl/3d-tiles/node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" - }, - "node_modules/@loaders.gl/compression": { + "node_modules/@loaders.gl/i3s/node_modules/@loaders.gl/compression": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@loaders.gl/compression/-/compression-4.3.3.tgz", "integrity": "sha512-1IZIFb6MaIiwMwsLEUk5Tyu8qlY7ge2S2Uy2qJxTP23CHakdocue89c54ygo0CgOiUw3Tr1r5JVa3EhB4+lOJQ==", + "license": "MIT", + "peer": true, "dependencies": { "@loaders.gl/loader-utils": "4.3.3", "@loaders.gl/worker-utils": "4.3.3", @@ -1582,21 +2060,12 @@ "@loaders.gl/core": "^4.3.0" } }, - "node_modules/@loaders.gl/core": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/core/-/core-4.3.3.tgz", - "integrity": "sha512-RaQ3uNg4ZaVqDRgvJ2CjaOjeeHdKvbKuzFFgbGnflVB9is5bu+h3EKc3Jke7NGVvLBsZ6oIXzkwHijVsMfxv8g==", - "dependencies": { - "@loaders.gl/loader-utils": "4.3.3", - "@loaders.gl/schema": "4.3.3", - "@loaders.gl/worker-utils": "4.3.3", - "@probe.gl/log": "^4.0.2" - } - }, - "node_modules/@loaders.gl/crypto": { + "node_modules/@loaders.gl/i3s/node_modules/@loaders.gl/crypto": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@loaders.gl/crypto/-/crypto-4.3.3.tgz", "integrity": "sha512-uwqcSGJ4DdS2g3BYc4Noa4EGfnbK63wCQnke4Xyc7KTNl6P70oblDlRbL3df1WQPMTUoXYOERE+ei7Q0Tee4vQ==", + "license": "MIT", + "peer": true, "dependencies": { "@loaders.gl/loader-utils": "4.3.3", "@loaders.gl/worker-utils": "4.3.3", @@ -1606,10 +2075,12 @@ "@loaders.gl/core": "^4.3.0" } }, - "node_modules/@loaders.gl/draco": { + "node_modules/@loaders.gl/i3s/node_modules/@loaders.gl/draco": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@loaders.gl/draco/-/draco-4.3.3.tgz", "integrity": "sha512-f2isxvOoH4Pm5p4mGvNN9gVigUwX84j9gdKNMV1aSo56GS1KE3GS2rXaIoy1qaIHMzkPySUTEcOTwayf0hWU7A==", + "license": "MIT", + "peer": true, "dependencies": { "@loaders.gl/loader-utils": "4.3.3", "@loaders.gl/schema": "4.3.3", @@ -1620,195 +2091,257 @@ "@loaders.gl/core": "^4.3.0" } }, - "node_modules/@loaders.gl/gis": { + "node_modules/@loaders.gl/i3s/node_modules/@loaders.gl/images": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/gis/-/gis-4.3.3.tgz", - "integrity": "sha512-OQNrieRMihsy2mVHuhi7d/SThUdNCgFXmUqhCG53qAVIS+7Nm//lO9zty3EzfOGWHjYcx6+nxl4QO3mR5fXMvg==", + "resolved": "https://registry.npmjs.org/@loaders.gl/images/-/images-4.3.3.tgz", + "integrity": "sha512-s4InjIXqEu0T7anZLj4OBUuDBt2BNnAD0GLzSexSkBfQZfpXY0XJNl4mMf5nUKb5NDfXhIKIqv8y324US+I28A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@loaders.gl/loader-utils": "4.3.3" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.3.0" + } + }, + "node_modules/@loaders.gl/i3s/node_modules/@loaders.gl/loader-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@loaders.gl/loader-utils/-/loader-utils-4.3.3.tgz", + "integrity": "sha512-8erUIwWLiIsZX36fFa/seZsfTsWlLk72Sibh/YZJrPAefuVucV4mGGzMBZ96LE2BUfJhadn250eio/59TUFbNw==", + "license": "MIT", + "peer": true, "dependencies": { - "@loaders.gl/loader-utils": "4.3.3", "@loaders.gl/schema": "4.3.3", - "@mapbox/vector-tile": "^1.3.1", - "@math.gl/polygon": "^4.1.0", - "pbf": "^3.2.1" + "@loaders.gl/worker-utils": "4.3.3", + "@probe.gl/log": "^4.0.2", + "@probe.gl/stats": "^4.0.2" }, "peerDependencies": { "@loaders.gl/core": "^4.3.0" } }, - "node_modules/@loaders.gl/gltf": { + "node_modules/@loaders.gl/i3s/node_modules/@loaders.gl/math": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/gltf/-/gltf-4.3.3.tgz", - "integrity": "sha512-M7jQ7KIB5itctDmGYuT9gndmjNwk1lwQ+BV4l5CoFp38e4xJESPglj2Kj8csWdm3WJhrxIYEP4GpjXK02n8DSQ==", + "resolved": "https://registry.npmjs.org/@loaders.gl/math/-/math-4.3.3.tgz", + "integrity": "sha512-oUfCFYsybm6fKnYHU1BzqXsh0sCJ+M9CXNnD/083ZNW+lWdxD44eeTE3DdFYPEMe+yyMkLSGx/8WTMv7ev2t5Q==", + "license": "MIT", + "peer": true, "dependencies": { - "@loaders.gl/draco": "4.3.3", "@loaders.gl/images": "4.3.3", "@loaders.gl/loader-utils": "4.3.3", - "@loaders.gl/schema": "4.3.3", - "@loaders.gl/textures": "4.3.3", "@math.gl/core": "^4.1.0" }, "peerDependencies": { "@loaders.gl/core": "^4.3.0" } }, - "node_modules/@loaders.gl/i3s": { + "node_modules/@loaders.gl/i3s/node_modules/@loaders.gl/schema": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@loaders.gl/schema/-/schema-4.3.3.tgz", + "integrity": "sha512-zacc9/8je+VbuC6N/QRfiTjRd+BuxsYlddLX1u5/X/cg9s36WZZBlU1oNKUgTYe8eO6+qLyYx77yi+9JbbEehw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/geojson": "^7946.0.7" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.3.0" + } + }, + "node_modules/@loaders.gl/i3s/node_modules/@loaders.gl/textures": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@loaders.gl/textures/-/textures-4.3.3.tgz", + "integrity": "sha512-qIo4ehzZnXFpPKl1BGQG4G3cAhBSczO9mr+H/bT7qFwtSirWVlqsvMlx1Q4VpmouDu+tudwwOlq7B3yqU5P5yQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@loaders.gl/images": "4.3.3", + "@loaders.gl/loader-utils": "4.3.3", + "@loaders.gl/schema": "4.3.3", + "@loaders.gl/worker-utils": "4.3.3", + "@math.gl/types": "^4.1.0", + "ktx-parse": "^0.7.0", + "texture-compressor": "^1.0.2" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.3.0" + } + }, + "node_modules/@loaders.gl/i3s/node_modules/@loaders.gl/tiles": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@loaders.gl/tiles/-/tiles-4.3.3.tgz", + "integrity": "sha512-cmC/spc+DM5aCSHoHrEuTPhDLuZRtkrWnlHkhC2Tur9uiUr41U3vXnC5slJkOeIWkaN4Q7KRFGCQ6SCendYfMg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@loaders.gl/loader-utils": "4.3.3", + "@loaders.gl/math": "4.3.3", + "@math.gl/core": "^4.1.0", + "@math.gl/culling": "^4.1.0", + "@math.gl/geospatial": "^4.1.0", + "@math.gl/web-mercator": "^4.1.0", + "@probe.gl/stats": "^4.0.2" + }, + "peerDependencies": { + "@loaders.gl/core": "^4.3.0" + } + }, + "node_modules/@loaders.gl/i3s/node_modules/@loaders.gl/worker-utils": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/i3s/-/i3s-4.3.3.tgz", - "integrity": "sha512-un4CoCxKNrTW4nhMTwQxRqPWA9/P2oG6+p5NJjnPPbEcs9QvrHJVXzCnzIpt2nvW7gyZyEHXfwBkVqGrZN48ng==", + "resolved": "https://registry.npmjs.org/@loaders.gl/worker-utils/-/worker-utils-4.3.3.tgz", + "integrity": "sha512-eg45Ux6xqsAfqPUqJkhmbFZh9qfmYuPfA+34VcLtfeXIwAngeP6o4SrTmm9LWLGUKiSh47anCEV1p7borDgvGQ==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "@loaders.gl/core": "^4.3.0" + } + }, + "node_modules/@loaders.gl/i3s/node_modules/@loaders.gl/zip": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@loaders.gl/zip/-/zip-4.3.3.tgz", + "integrity": "sha512-PPNR9xBLfhBd4Fw69Ai5cUzIJZFCYg3DiYGeR8mA8ik9tuseH+hEBUSsmzU4RFP53xkPLLYvzXjVyiBzfbsjZg==", + "license": "MIT", "peer": true, "dependencies": { "@loaders.gl/compression": "4.3.3", "@loaders.gl/crypto": "4.3.3", - "@loaders.gl/draco": "4.3.3", - "@loaders.gl/images": "4.3.3", "@loaders.gl/loader-utils": "4.3.3", - "@loaders.gl/math": "4.3.3", - "@loaders.gl/schema": "4.3.3", - "@loaders.gl/textures": "4.3.3", - "@loaders.gl/tiles": "4.3.3", - "@loaders.gl/zip": "4.3.3", - "@math.gl/core": "^4.1.0", - "@math.gl/culling": "^4.1.0", - "@math.gl/geospatial": "^4.1.0" + "jszip": "^3.1.5", + "md5": "^2.3.0" }, "peerDependencies": { "@loaders.gl/core": "^4.3.0" } }, + "node_modules/@loaders.gl/i3s/node_modules/ktx-parse": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.7.1.tgz", + "integrity": "sha512-FeA3g56ksdFNwjXJJsc1CCc7co+AJYDp6ipIp878zZ2bU8kWROatLYf39TQEd4/XRSUvBXovQ8gaVKWPXsCLEQ==", + "license": "MIT", + "peer": true + }, "node_modules/@loaders.gl/images": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/images/-/images-4.3.3.tgz", - "integrity": "sha512-s4InjIXqEu0T7anZLj4OBUuDBt2BNnAD0GLzSexSkBfQZfpXY0XJNl4mMf5nUKb5NDfXhIKIqv8y324US+I28A==", + "version": "4.2.5", + "license": "MIT", "dependencies": { - "@loaders.gl/loader-utils": "4.3.3" + "@loaders.gl/loader-utils": "4.2.5" }, "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@loaders.gl/loader-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/loader-utils/-/loader-utils-4.3.3.tgz", - "integrity": "sha512-8erUIwWLiIsZX36fFa/seZsfTsWlLk72Sibh/YZJrPAefuVucV4mGGzMBZ96LE2BUfJhadn250eio/59TUFbNw==", + "version": "4.2.5", + "license": "MIT", "dependencies": { - "@loaders.gl/schema": "4.3.3", - "@loaders.gl/worker-utils": "4.3.3", - "@probe.gl/log": "^4.0.2", + "@loaders.gl/schema": "4.2.5", + "@loaders.gl/worker-utils": "4.2.5", "@probe.gl/stats": "^4.0.2" }, "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@loaders.gl/math": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/math/-/math-4.3.3.tgz", - "integrity": "sha512-oUfCFYsybm6fKnYHU1BzqXsh0sCJ+M9CXNnD/083ZNW+lWdxD44eeTE3DdFYPEMe+yyMkLSGx/8WTMv7ev2t5Q==", + "version": "4.2.5", + "license": "MIT", "dependencies": { - "@loaders.gl/images": "4.3.3", - "@loaders.gl/loader-utils": "4.3.3", - "@math.gl/core": "^4.1.0" + "@loaders.gl/images": "4.2.5", + "@loaders.gl/loader-utils": "4.2.5", + "@math.gl/core": "^4.0.0" }, "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@loaders.gl/mvt": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/mvt/-/mvt-4.3.3.tgz", - "integrity": "sha512-y7YtrpPBOR4ek1Vj8vM2dRFrFfZHz7e5ZuYSgANOAyGzDXbnZ5TKPPIQC8Plm/y3ZQe+063yJ+kuGc91FBRbXg==", + "version": "4.2.5", + "license": "MIT", "dependencies": { - "@loaders.gl/gis": "4.3.3", - "@loaders.gl/images": "4.3.3", - "@loaders.gl/loader-utils": "4.3.3", - "@loaders.gl/schema": "4.3.3", - "@math.gl/polygon": "^4.1.0", - "@probe.gl/stats": "^4.0.0", + "@loaders.gl/gis": "4.2.5", + "@loaders.gl/images": "4.2.5", + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/schema": "4.2.5", + "@math.gl/polygon": "^4.0.0", "pbf": "^3.2.1" }, "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@loaders.gl/schema": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/schema/-/schema-4.3.3.tgz", - "integrity": "sha512-zacc9/8je+VbuC6N/QRfiTjRd+BuxsYlddLX1u5/X/cg9s36WZZBlU1oNKUgTYe8eO6+qLyYx77yi+9JbbEehw==", + "version": "4.2.5", + "license": "MIT", "dependencies": { "@types/geojson": "^7946.0.7" }, "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@loaders.gl/terrain": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/terrain/-/terrain-4.3.3.tgz", - "integrity": "sha512-qPfpYL0imojyic0dTW71d9M8k2SY+wD60m31658vtsMogdVeBiAX/WYtk8W/NKcqBS8FMv9CC41PlULrvcZ7TQ==", + "version": "4.2.5", + "license": "MIT", "dependencies": { - "@loaders.gl/images": "4.3.3", - "@loaders.gl/loader-utils": "4.3.3", - "@loaders.gl/schema": "4.3.3", + "@loaders.gl/images": "4.2.5", + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/schema": "4.2.5", "@mapbox/martini": "^0.2.0" }, "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@loaders.gl/textures": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/textures/-/textures-4.3.3.tgz", - "integrity": "sha512-qIo4ehzZnXFpPKl1BGQG4G3cAhBSczO9mr+H/bT7qFwtSirWVlqsvMlx1Q4VpmouDu+tudwwOlq7B3yqU5P5yQ==", + "version": "4.2.5", + "license": "MIT", "dependencies": { - "@loaders.gl/images": "4.3.3", - "@loaders.gl/loader-utils": "4.3.3", - "@loaders.gl/schema": "4.3.3", - "@loaders.gl/worker-utils": "4.3.3", - "@math.gl/types": "^4.1.0", - "ktx-parse": "^0.7.0", + "@loaders.gl/images": "4.2.5", + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/schema": "4.2.5", + "@loaders.gl/worker-utils": "4.2.5", + "@math.gl/types": "^4.0.1", + "ktx-parse": "^0.0.4", "texture-compressor": "^1.0.2" }, "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@loaders.gl/tiles": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/tiles/-/tiles-4.3.3.tgz", - "integrity": "sha512-cmC/spc+DM5aCSHoHrEuTPhDLuZRtkrWnlHkhC2Tur9uiUr41U3vXnC5slJkOeIWkaN4Q7KRFGCQ6SCendYfMg==", + "version": "4.2.5", + "license": "MIT", "dependencies": { - "@loaders.gl/loader-utils": "4.3.3", - "@loaders.gl/math": "4.3.3", - "@math.gl/core": "^4.1.0", - "@math.gl/culling": "^4.1.0", - "@math.gl/geospatial": "^4.1.0", - "@math.gl/web-mercator": "^4.1.0", + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/math": "4.2.5", + "@math.gl/core": "^4.0.0", + "@math.gl/culling": "^4.0.0", + "@math.gl/geospatial": "^4.0.0", + "@math.gl/web-mercator": "^4.0.0", "@probe.gl/stats": "^4.0.2" }, "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@loaders.gl/wms": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/wms/-/wms-4.3.3.tgz", - "integrity": "sha512-SmpdFB/Jhtzbc52TlMKRSxQkUDfYP/FN8qdTdL3PtVN74Vuh4eZ8t7nLzplUCx0tbkbT1D7nfreSU4ndWq2zjQ==", + "version": "4.2.5", + "license": "MIT", "dependencies": { - "@loaders.gl/images": "4.3.3", - "@loaders.gl/loader-utils": "4.3.3", - "@loaders.gl/schema": "4.3.3", - "@loaders.gl/xml": "4.3.3", + "@loaders.gl/images": "4.2.5", + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/schema": "4.2.5", + "@loaders.gl/xml": "4.2.5", "@turf/rewind": "^5.1.5", "deep-strict-equal": "^0.2.0" }, "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@loaders.gl/wms/node_modules/@turf/boolean-clockwise": { "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-5.1.5.tgz", - "integrity": "sha512-FqbmEEOJ4rU4/2t7FKx0HUWmjFEVqR+NJrFP7ymGSjja2SQ7Q91nnBihGuT+yuHHl6ElMjQ3ttsB/eTmyCycxA==", + "license": "MIT", "dependencies": { "@turf/helpers": "^5.1.5", "@turf/invariant": "^5.1.5" @@ -1816,37 +2349,32 @@ }, "node_modules/@loaders.gl/wms/node_modules/@turf/clone": { "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-5.1.5.tgz", - "integrity": "sha512-//pITsQ8xUdcQ9pVb4JqXiSqG4dos5Q9N4sYFoWghX21tfOV2dhc5TGqYOhnHrQS7RiKQL1vQ48kIK34gQ5oRg==", + "license": "MIT", "dependencies": { "@turf/helpers": "^5.1.5" } }, "node_modules/@loaders.gl/wms/node_modules/@turf/helpers": { "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz", - "integrity": "sha512-/lF+JR+qNDHZ8bF9d+Cp58nxtZWJ3sqFe6n3u3Vpj+/0cqkjk4nXKYBSY0azm+GIYB5mWKxUXvuP/m0ZnKj1bw==" + "license": "MIT" }, "node_modules/@loaders.gl/wms/node_modules/@turf/invariant": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-5.2.0.tgz", - "integrity": "sha512-28RCBGvCYsajVkw2EydpzLdcYyhSA77LovuOvgCJplJWaNVyJYH6BOR3HR9w50MEkPqb/Vc/jdo6I6ermlRtQA==", + "license": "MIT", "dependencies": { "@turf/helpers": "^5.1.5" } }, "node_modules/@loaders.gl/wms/node_modules/@turf/meta": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-5.2.0.tgz", - "integrity": "sha512-ZjQ3Ii62X9FjnK4hhdsbT+64AYRpaI8XMBMcyftEOGSmPMUVnkbvuv3C9geuElAXfQU7Zk1oWGOcrGOD9zr78Q==", + "license": "MIT", "dependencies": { "@turf/helpers": "^5.1.5" } }, "node_modules/@loaders.gl/wms/node_modules/@turf/rewind": { "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-5.1.5.tgz", - "integrity": "sha512-Gdem7JXNu+G4hMllQHXRFRihJl3+pNl7qY+l4qhQFxq+hiU1cQoVFnyoleIqWKIrdK/i2YubaSwc3SCM7N5mMw==", + "license": "MIT", "dependencies": { "@turf/boolean-clockwise": "^5.1.5", "@turf/clone": "^5.1.5", @@ -1856,112 +2384,92 @@ } }, "node_modules/@loaders.gl/worker-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/worker-utils/-/worker-utils-4.3.3.tgz", - "integrity": "sha512-eg45Ux6xqsAfqPUqJkhmbFZh9qfmYuPfA+34VcLtfeXIwAngeP6o4SrTmm9LWLGUKiSh47anCEV1p7borDgvGQ==", + "version": "4.2.5", + "license": "MIT", "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@loaders.gl/xml": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/xml/-/xml-4.3.3.tgz", - "integrity": "sha512-p4GjJn7cElnSxZE2DVsTPWnEJWL3iqTVnGbW2ODHFpW2E7ClPmyoDsUxb8zdW8DuQKfLPJkUILtubbaHmwOwZg==", + "version": "4.2.5", + "license": "MIT", "dependencies": { - "@loaders.gl/loader-utils": "4.3.3", - "@loaders.gl/schema": "4.3.3", + "@loaders.gl/loader-utils": "4.2.5", + "@loaders.gl/schema": "4.2.5", "fast-xml-parser": "^4.2.5" }, "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@loaders.gl/zip": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@loaders.gl/zip/-/zip-4.3.3.tgz", - "integrity": "sha512-PPNR9xBLfhBd4Fw69Ai5cUzIJZFCYg3DiYGeR8mA8ik9tuseH+hEBUSsmzU4RFP53xkPLLYvzXjVyiBzfbsjZg==", + "version": "4.2.5", + "license": "MIT", "dependencies": { - "@loaders.gl/compression": "4.3.3", - "@loaders.gl/crypto": "4.3.3", - "@loaders.gl/loader-utils": "4.3.3", + "@loaders.gl/compression": "4.2.5", + "@loaders.gl/crypto": "4.2.5", + "@loaders.gl/loader-utils": "4.2.5", "jszip": "^3.1.5", "md5": "^2.3.0" }, "peerDependencies": { - "@loaders.gl/core": "^4.3.0" + "@loaders.gl/core": "^4.0.0" } }, "node_modules/@luma.gl/constants": { - "version": "9.1.9", - "resolved": "https://registry.npmjs.org/@luma.gl/constants/-/constants-9.1.9.tgz", - "integrity": "sha512-yc9fml04OeTTcwK+7gmDMxoLQ67j4ZiAFXjmYvPomYyBVzS0NZxTDuwcCBmnxjLOiroOZW8FRRrVc/yOiFug2w==" + "version": "9.0.27", + "license": "MIT" }, "node_modules/@luma.gl/core": { - "version": "9.1.9", - "resolved": "https://registry.npmjs.org/@luma.gl/core/-/core-9.1.9.tgz", - "integrity": "sha512-1i9N7+I/UbFjx3axSMlc3/NufA+C2iBv/7mw51gRE/ypQPgvFmY/QqXBVZRe+nthF+OhlUMhO19TBndzYFTWhA==", + "version": "9.0.27", + "license": "MIT", + "peer": true, "dependencies": { - "@math.gl/types": "^4.1.0", - "@probe.gl/env": "^4.0.8", - "@probe.gl/log": "^4.0.8", - "@probe.gl/stats": "^4.0.8", + "@math.gl/types": "^4.0.0", + "@probe.gl/env": "^4.0.2", + "@probe.gl/log": "^4.0.2", + "@probe.gl/stats": "^4.0.2", "@types/offscreencanvas": "^2019.6.4" } }, "node_modules/@luma.gl/engine": { - "version": "9.1.9", - "resolved": "https://registry.npmjs.org/@luma.gl/engine/-/engine-9.1.9.tgz", - "integrity": "sha512-n1GLK1sUMFkWxdb+aZYn6ZBFltFEMi7X+6ZPxn2pBsNT6oeF4AyvH5AyqhOpvHvUnCLDt3Zsf1UIfx3MI//YSw==", + "version": "9.0.27", + "license": "MIT", + "peer": true, "dependencies": { - "@math.gl/core": "^4.1.0", - "@math.gl/types": "^4.1.0", - "@probe.gl/log": "^4.0.8", - "@probe.gl/stats": "^4.0.8" + "@luma.gl/shadertools": "9.0.27", + "@math.gl/core": "^4.0.0", + "@probe.gl/log": "^4.0.2", + "@probe.gl/stats": "^4.0.2" }, "peerDependencies": { - "@luma.gl/core": "^9.1.0", - "@luma.gl/shadertools": "^9.1.0" + "@luma.gl/core": "^9.0.0" } }, "node_modules/@luma.gl/gltf": { - "version": "9.1.9", - "resolved": "https://registry.npmjs.org/@luma.gl/gltf/-/gltf-9.1.9.tgz", - "integrity": "sha512-KgVBIFCtRO1oadgMDycMJA5s+q519l/fQBGAZpUcLfWsaEDQfdHW2NLdrK/00VDv46Ng8tN/O6uyH6E40uLcLw==", + "version": "9.0.27", + "license": "MIT", "dependencies": { - "@loaders.gl/core": "^4.2.0", "@loaders.gl/textures": "^4.2.0", - "@math.gl/core": "^4.1.0" + "@luma.gl/shadertools": "9.0.27", + "@math.gl/core": "^4.0.0" }, "peerDependencies": { - "@luma.gl/core": "^9.1.0", - "@luma.gl/engine": "^9.1.0", - "@luma.gl/shadertools": "^9.1.0" + "@loaders.gl/core": "^4.2.0", + "@luma.gl/core": "^9.0.0", + "@luma.gl/engine": "^9.0.0" } }, "node_modules/@luma.gl/shadertools": { - "version": "9.1.9", - "resolved": "https://registry.npmjs.org/@luma.gl/shadertools/-/shadertools-9.1.9.tgz", - "integrity": "sha512-Uqp2xfgIEunRMLXTeCJ4uEMlWcUGcYMZGJ8GAOrAeDzn4bMKVRKmZDC71vkuTctnaodM3UdrI9W6s1sJlrXsxw==", - "dependencies": { - "@math.gl/core": "^4.1.0", - "@math.gl/types": "^4.1.0", - "wgsl_reflect": "^1.2.0" - }, - "peerDependencies": { - "@luma.gl/core": "^9.1.0" - } - }, - "node_modules/@luma.gl/webgl": { - "version": "9.1.9", - "resolved": "https://registry.npmjs.org/@luma.gl/webgl/-/webgl-9.1.9.tgz", - "integrity": "sha512-jecHjhNSWkXH0v62rM6G5fIIkOmsrND27099iKgdutFvHIvd4QS4UzGWEEa9AEPlP0rTLqXkA6y6YL7f42ZkVg==", + "version": "9.0.27", + "license": "MIT", "dependencies": { - "@luma.gl/constants": "9.1.9", - "@math.gl/types": "^4.1.0", - "@probe.gl/env": "^4.0.8" + "@math.gl/core": "^4.0.0", + "@math.gl/types": "^4.0.0", + "wgsl_reflect": "^1.0.1" }, "peerDependencies": { - "@luma.gl/core": "^9.1.0" + "@luma.gl/core": "^9.0.0" } }, "node_modules/@mapbox/geojson-rewind": { @@ -2003,8 +2511,7 @@ }, "node_modules/@mapbox/martini": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@mapbox/martini/-/martini-0.2.0.tgz", - "integrity": "sha512-7hFhtkb0KTLEls+TRw/rWayq5EeHtTaErgm/NskVoXmtgAQu/9D299aeyj6mzAR/6XUnYRp2lU+4IcrYRFjVsQ==" + "license": "ISC" }, "node_modules/@mapbox/point-geometry": { "version": "0.1.0", @@ -2077,8 +2584,7 @@ }, "node_modules/@math.gl/culling": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@math.gl/culling/-/culling-4.1.0.tgz", - "integrity": "sha512-jFmjFEACnP9kVl8qhZxFNhCyd47qPfSVmSvvjR0/dIL6R9oD5zhR1ub2gN16eKDO/UM7JF9OHKU3EBIfeR7gtg==", + "license": "MIT", "dependencies": { "@math.gl/core": "4.1.0", "@math.gl/types": "4.1.0" @@ -2086,8 +2592,7 @@ }, "node_modules/@math.gl/geospatial": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@math.gl/geospatial/-/geospatial-4.1.0.tgz", - "integrity": "sha512-BzsUhpVvnmleyYF6qdqJIip6FtIzJmnWuPTGhlBuPzh7VBHLonCFSPtQpbkRuoyAlbSyaGXcVt6p6lm9eK2vtg==", + "license": "MIT", "dependencies": { "@math.gl/core": "4.1.0", "@math.gl/types": "4.1.0" @@ -2095,8 +2600,7 @@ }, "node_modules/@math.gl/polygon": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@math.gl/polygon/-/polygon-4.1.0.tgz", - "integrity": "sha512-YA/9PzaCRHbIP5/0E9uTYrqe+jsYTQoqoDWhf6/b0Ixz8bPZBaGDEafLg3z7ffBomZLacUty9U3TlPjqMtzPjA==", + "license": "MIT", "dependencies": { "@math.gl/core": "4.1.0" } @@ -3005,12 +3509,14 @@ "node_modules/@probe.gl/env": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@probe.gl/env/-/env-4.1.0.tgz", - "integrity": "sha512-5ac2Jm2K72VCs4eSMsM7ykVRrV47w32xOGMvcgqn8vQdEMF9PRXyBGYEV9YbqRKWNKpNKmQJVi4AHM/fkCxs9w==" + "integrity": "sha512-5ac2Jm2K72VCs4eSMsM7ykVRrV47w32xOGMvcgqn8vQdEMF9PRXyBGYEV9YbqRKWNKpNKmQJVi4AHM/fkCxs9w==", + "license": "MIT" }, "node_modules/@probe.gl/log": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@probe.gl/log/-/log-4.1.0.tgz", "integrity": "sha512-r4gRReNY6f+OZEMgfWEXrAE2qJEt8rX0HsDJQXUBMoc+5H47bdB7f/5HBHAmapK8UydwPKL9wCDoS22rJ0yq7Q==", + "license": "MIT", "dependencies": { "@probe.gl/env": "4.1.0" } @@ -3018,12 +3524,14 @@ "node_modules/@probe.gl/stats": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@probe.gl/stats/-/stats-4.1.0.tgz", - "integrity": "sha512-EI413MkWKBDVNIfLdqbeNSJTs7ToBz/KVGkwi3D+dQrSIkRI2IYbWGAU3xX+D6+CI4ls8ehxMhNpUVMaZggDvQ==" + "integrity": "sha512-EI413MkWKBDVNIfLdqbeNSJTs7ToBz/KVGkwi3D+dQrSIkRI2IYbWGAU3xX+D6+CI4ls8ehxMhNpUVMaZggDvQ==", + "license": "MIT" }, "node_modules/@react-aria/breadcrumbs": { "version": "3.5.23", "resolved": "https://registry.npmjs.org/@react-aria/breadcrumbs/-/breadcrumbs-3.5.23.tgz", "integrity": "sha512-4uLxuAgPfXds8sBc/Cg0ml7LKWzK+YTwHL7xclhQUkPO32rzlHDl+BJ5cyWhvZgGUf8JJXbXhD5VlJJzbbl8Xg==", + "license": "Apache-2.0", "dependencies": { "@react-aria/i18n": "^3.12.8", "@react-aria/link": "^3.8.0", @@ -3041,6 +3549,7 @@ "version": "3.13.0", "resolved": "https://registry.npmjs.org/@react-aria/button/-/button-3.13.0.tgz", "integrity": "sha512-BEcTQb7Q8ZrAtn0scPDv/ErZoGC1FI0sLk0UTPGskuh/RV9ZZGFbuSWTqOwV8w5CS6VMvPjH6vaE8hS7sb5DIw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/interactions": "^3.25.0", "@react-aria/toolbar": "3.0.0-beta.15", @@ -3059,6 +3568,7 @@ "version": "3.8.0", "resolved": "https://registry.npmjs.org/@react-aria/calendar/-/calendar-3.8.0.tgz", "integrity": "sha512-9vms/fWjJPZkJcMxciwWWOjGy/Q0nqI6FV0pYbMZbqepkzglEaVd98kl506r/4hLhWKwLdTfqCgbntRecj8jBg==", + "license": "Apache-2.0", "dependencies": { "@internationalized/date": "^3.8.0", "@react-aria/i18n": "^3.12.8", @@ -3080,6 +3590,7 @@ "version": "3.15.4", "resolved": "https://registry.npmjs.org/@react-aria/checkbox/-/checkbox-3.15.4.tgz", "integrity": "sha512-ZkDJFs2EfMBXVIpBSo4ouB+NXyr2LRgZNp2x8/v+7n3aTmMU8j2PzT+Ra2geTQbC0glMP7UrSg4qZblqrxEBcQ==", + "license": "Apache-2.0", "dependencies": { "@react-aria/form": "^3.0.15", "@react-aria/interactions": "^3.25.0", @@ -3102,6 +3613,7 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/@react-aria/color/-/color-3.0.6.tgz", "integrity": "sha512-ik4Db9hrN1yIT0CQMB888ktBmrwA/kNhkfiDACtoUHv8Ev+YEpmagnmih9vMyW2vcnozYJpnn/aCMl59J5uMew==", + "license": "Apache-2.0", "dependencies": { "@react-aria/i18n": "^3.12.8", "@react-aria/interactions": "^3.25.0", @@ -3126,6 +3638,7 @@ "version": "3.12.2", "resolved": "https://registry.npmjs.org/@react-aria/combobox/-/combobox-3.12.2.tgz", "integrity": "sha512-EgddiF8VnAjB4EynJERPn4IoDMUabI8GiKOQZ6Ar3MlRWxQnUfxPpZwXs8qWR3dPCzYUt2PhBinhBMjyR1yRIw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/focus": "^3.20.2", "@react-aria/i18n": "^3.12.8", @@ -3153,6 +3666,7 @@ "version": "3.14.2", "resolved": "https://registry.npmjs.org/@react-aria/datepicker/-/datepicker-3.14.2.tgz", "integrity": "sha512-O7fdzcqIJ7i/+8SGYvx4tloTZgK4Ws8OChdbFcd2rZoRPqxM50M6J+Ota8hTet2wIhojUXnM3x2na3EvoucBXA==", + "license": "Apache-2.0", "dependencies": { "@internationalized/date": "^3.8.0", "@internationalized/number": "^3.6.1", @@ -3182,6 +3696,7 @@ "version": "3.5.24", "resolved": "https://registry.npmjs.org/@react-aria/dialog/-/dialog-3.5.24.tgz", "integrity": "sha512-tw0WH89gVpHMI5KUQhuzRE+IYCc9clRfDvCppuXNueKDrZmrQKbeoU6d0b5WYRsBur2+d7ErtvpLzHVqE1HzfA==", + "license": "Apache-2.0", "dependencies": { "@react-aria/interactions": "^3.25.0", "@react-aria/overlays": "^3.27.0", @@ -3199,6 +3714,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@react-aria/disclosure/-/disclosure-3.0.4.tgz", "integrity": "sha512-HXGVLA06BH0b/gN8dCTzWATwMikz8D+ahRxZiI0HDZxLADWGsSPqRXKN0GNAiBKbvPtvAbrwslE3pktk/SlU/w==", + "license": "Apache-2.0", "dependencies": { "@react-aria/ssr": "^3.9.8", "@react-aria/utils": "^3.28.2", @@ -3215,6 +3731,7 @@ "version": "3.9.2", "resolved": "https://registry.npmjs.org/@react-aria/dnd/-/dnd-3.9.2.tgz", "integrity": "sha512-pPYygmJTjSPV2K/r48TvF75WuddG8d8nlIxAXSW22++WKqZ0z+eun6gDUXoKeB2rgY7sVfLqpRdnPV52AnBX+Q==", + "license": "Apache-2.0", "dependencies": { "@internationalized/string": "^3.2.6", "@react-aria/i18n": "^3.12.8", @@ -3236,6 +3753,7 @@ "version": "3.20.2", "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.20.2.tgz", "integrity": "sha512-Q3rouk/rzoF/3TuH6FzoAIKrl+kzZi9LHmr8S5EqLAOyP9TXIKG34x2j42dZsAhrw7TbF9gA8tBKwnCNH4ZV+Q==", + "license": "Apache-2.0", "dependencies": { "@react-aria/interactions": "^3.25.0", "@react-aria/utils": "^3.28.2", @@ -3252,6 +3770,7 @@ "version": "3.0.15", "resolved": "https://registry.npmjs.org/@react-aria/form/-/form-3.0.15.tgz", "integrity": "sha512-kk8AnLz+EOgnn3sTaXYmtw+YzVDc1of/+xAkuOupQi6zQFnNRjc99JlDbKHoUZ39urMl+8lsp/1b9VPPhNrBNw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/interactions": "^3.25.0", "@react-aria/utils": "^3.28.2", @@ -3268,6 +3787,7 @@ "version": "3.13.0", "resolved": "https://registry.npmjs.org/@react-aria/grid/-/grid-3.13.0.tgz", "integrity": "sha512-RcuJYA4fyJ83MH3SunU+P5BGkx3LJdQ6kxwqwWGIuI9eUKc7uVbqvN9WN3fI+L0QfxqBFmh7ffRxIdQn7puuzw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/focus": "^3.20.2", "@react-aria/i18n": "^3.12.8", @@ -3292,6 +3812,7 @@ "version": "3.12.0", "resolved": "https://registry.npmjs.org/@react-aria/gridlist/-/gridlist-3.12.0.tgz", "integrity": "sha512-KSpnSBYQ7ozGQNaRR2NGq7Fl2zIv5w9KNyO9V/IE2mxUNfX6fwqUPoANFcy9ySosksE7pPnFtuYIB+TQtUjYqQ==", + "license": "Apache-2.0", "dependencies": { "@react-aria/focus": "^3.20.2", "@react-aria/grid": "^3.13.0", @@ -3314,6 +3835,7 @@ "version": "3.12.8", "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.8.tgz", "integrity": "sha512-V/Nau9WuwTwxfFffQL4URyKyY2HhRlu9zmzkF2Hw/j5KmEQemD+9jfaLueG2CJu85lYL06JrZXUdnhZgKnqMkA==", + "license": "Apache-2.0", "dependencies": { "@internationalized/date": "^3.8.0", "@internationalized/message": "^3.1.7", @@ -3333,6 +3855,7 @@ "version": "3.25.0", "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.0.tgz", "integrity": "sha512-GgIsDLlO8rDU/nFn6DfsbP9rfnzhm8QFjZkB9K9+r+MTSCn7bMntiWQgMM+5O6BiA8d7C7x4zuN4bZtc0RBdXQ==", + "license": "Apache-2.0", "dependencies": { "@react-aria/ssr": "^3.9.8", "@react-aria/utils": "^3.28.2", @@ -3349,6 +3872,7 @@ "version": "3.7.17", "resolved": "https://registry.npmjs.org/@react-aria/label/-/label-3.7.17.tgz", "integrity": "sha512-Fz7IC2LQT2Y/sAoV+gFEXoULtkznzmK2MmeTv5shTNjeTxzB1BhQbD4wyCypi7eGsnD/9Zy+8viULCsIUbvjWw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/utils": "^3.28.2", "@react-types/shared": "^3.29.0", @@ -3363,6 +3887,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/@react-aria/landmark/-/landmark-3.0.2.tgz", "integrity": "sha512-KVXa9s3fSgo/PiUjdbnPh3a1yS4t2bMZeVBPPzYAgQ4wcU2WjuLkhviw+5GWSWRfT+jpIMV7R/cmyvr0UHvRfg==", + "license": "Apache-2.0", "dependencies": { "@react-aria/utils": "^3.28.2", "@react-types/shared": "^3.29.0", @@ -3378,6 +3903,7 @@ "version": "3.8.0", "resolved": "https://registry.npmjs.org/@react-aria/link/-/link-3.8.0.tgz", "integrity": "sha512-gpDD6t3FqtFR9QjSIKNpmSR3tS4JG2anVKx2wixuRDHO6Ddexxv4SBzsE1+230p+FlFGjftFa2lEgQ7RNjZrmA==", + "license": "Apache-2.0", "dependencies": { "@react-aria/interactions": "^3.25.0", "@react-aria/utils": "^3.28.2", @@ -3394,6 +3920,7 @@ "version": "3.14.3", "resolved": "https://registry.npmjs.org/@react-aria/listbox/-/listbox-3.14.3.tgz", "integrity": "sha512-wzelam1KENUvKjsTq8gfrOW2/iab8SyIaSXfFvGmWW82XlDTlW+oQeA39tvOZktMVGspr+xp8FySY09rtz6UXw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/interactions": "^3.25.0", "@react-aria/label": "^3.7.17", @@ -3414,6 +3941,7 @@ "version": "3.4.2", "resolved": "https://registry.npmjs.org/@react-aria/live-announcer/-/live-announcer-3.4.2.tgz", "integrity": "sha512-6+yNF9ZrZ4YJ60Oxy2gKI4/xy6WUv1iePDCFJkgpNVuOEYi8W8czff8ctXu/RPB25OJx5v2sCw9VirRogTo2zA==", + "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0" } @@ -3422,6 +3950,7 @@ "version": "3.18.2", "resolved": "https://registry.npmjs.org/@react-aria/menu/-/menu-3.18.2.tgz", "integrity": "sha512-90k+Ke1bhFWhR2zuRI6OwKWQrCpOD99n+9jhG96JZJZlNo5lB+5kS+ufG1LRv5GBnCug0ciLQmPMAfguVsCjEQ==", + "license": "Apache-2.0", "dependencies": { "@react-aria/focus": "^3.20.2", "@react-aria/i18n": "^3.12.8", @@ -3447,6 +3976,7 @@ "version": "3.4.22", "resolved": "https://registry.npmjs.org/@react-aria/meter/-/meter-3.4.22.tgz", "integrity": "sha512-A/30vrtJO0xqctS/ngE1Lp/w3Aq3MPcpdRHU5E06EUYotzRzHFE9sNmezWslkZ3NfYwA/mxLvgmrsOJSR0Hx6A==", + "license": "Apache-2.0", "dependencies": { "@react-aria/progress": "^3.4.22", "@react-types/meter": "^3.4.8", @@ -3462,6 +3992,7 @@ "version": "3.11.13", "resolved": "https://registry.npmjs.org/@react-aria/numberfield/-/numberfield-3.11.13.tgz", "integrity": "sha512-F73BVdIRV8VvKl0omhGaf0E7mdJ7pdPjDP3wYNf410t55BXPxmndItUKpGfxSbl8k6ZYLvQyOqkD6oWSfZXpZw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/i18n": "^3.12.8", "@react-aria/interactions": "^3.25.0", @@ -3484,6 +4015,7 @@ "version": "3.27.0", "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.27.0.tgz", "integrity": "sha512-2vZVgL7FrloN5Rh8sAhadGADJbuWg69DdSJB3fd2/h5VvcEhnIfNPu9Ma5XmdkApDoTboIEsKZ4QLYwRl98w6w==", + "license": "Apache-2.0", "dependencies": { "@react-aria/focus": "^3.20.2", "@react-aria/i18n": "^3.12.8", @@ -3506,6 +4038,7 @@ "version": "3.4.22", "resolved": "https://registry.npmjs.org/@react-aria/progress/-/progress-3.4.22.tgz", "integrity": "sha512-wK2hath4C9HKgmjCH+iSrAs86sUKqqsYKbEKk9/Rj9rzXqHyaEK9EG0YZDnSjd8kX+N9hYcs5MfJl6AZMH4juQ==", + "license": "Apache-2.0", "dependencies": { "@react-aria/i18n": "^3.12.8", "@react-aria/label": "^3.7.17", @@ -3523,6 +4056,7 @@ "version": "3.11.2", "resolved": "https://registry.npmjs.org/@react-aria/radio/-/radio-3.11.2.tgz", "integrity": "sha512-6AFJHXMewJBgHNhqkN1qjgwwx6kmagwYD+3Z+hNK1UHTsKe1Uud5/IF7gPFCqlZeKxA+Lvn9gWiqJrQbtD2+wg==", + "license": "Apache-2.0", "dependencies": { "@react-aria/focus": "^3.20.2", "@react-aria/form": "^3.0.15", @@ -3544,6 +4078,7 @@ "version": "3.8.3", "resolved": "https://registry.npmjs.org/@react-aria/searchfield/-/searchfield-3.8.3.tgz", "integrity": "sha512-t1DW3nUkPHyZhFhUbT+TdhvI8yZYvUPCuwl0FyraMRCQ4+ww5Ieu4n8JB9IGYmIUB/GWEbZlDHplu4s3efmliA==", + "license": "Apache-2.0", "dependencies": { "@react-aria/i18n": "^3.12.8", "@react-aria/textfield": "^3.17.2", @@ -3563,6 +4098,7 @@ "version": "3.15.4", "resolved": "https://registry.npmjs.org/@react-aria/select/-/select-3.15.4.tgz", "integrity": "sha512-CipqXgdOfWsiHw/chfqd8t9IQpvehP+3uKLJx3ic4Uyj+FT/SxVmmjX0gyvVbZd00ltFCMJYO2xYKQUlbW2AtQ==", + "license": "Apache-2.0", "dependencies": { "@react-aria/form": "^3.0.15", "@react-aria/i18n": "^3.12.8", @@ -3588,6 +4124,7 @@ "version": "3.24.0", "resolved": "https://registry.npmjs.org/@react-aria/selection/-/selection-3.24.0.tgz", "integrity": "sha512-RfGXVc04zz41NVIW89/a3quURZ4LT/GJLkiajQK2VjhisidPdrAWkcfjjWJj0n+tm5gPWbi9Rs5R/Rc8mrvq8Q==", + "license": "Apache-2.0", "dependencies": { "@react-aria/focus": "^3.20.2", "@react-aria/i18n": "^3.12.8", @@ -3606,6 +4143,7 @@ "version": "3.4.8", "resolved": "https://registry.npmjs.org/@react-aria/separator/-/separator-3.4.8.tgz", "integrity": "sha512-ncuOSTBF/qbNumnW/IRz+xyr+Ud85eCF0Expw4XWhKjAZfzJd86MxPY5ZsxE7pYLOcRWdOSIH1/obwwwSz8ALQ==", + "license": "Apache-2.0", "dependencies": { "@react-aria/utils": "^3.28.2", "@react-types/shared": "^3.29.0", @@ -3620,6 +4158,7 @@ "version": "3.7.18", "resolved": "https://registry.npmjs.org/@react-aria/slider/-/slider-3.7.18.tgz", "integrity": "sha512-GBVv5Rpvj/6JH2LnF1zVAhBmxGiuq7R8Ekqyr5kBrCc2ToF3PrTjfGc/mlh0eEtbj+NvAcnlgTx1/qosYt1sGw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/i18n": "^3.12.8", "@react-aria/interactions": "^3.25.0", @@ -3639,6 +4178,7 @@ "version": "3.6.14", "resolved": "https://registry.npmjs.org/@react-aria/spinbutton/-/spinbutton-3.6.14.tgz", "integrity": "sha512-oSKe9p0Q/7W39eXRnLxlwJG5dQo4ffosRT3u2AtOcFkk2Zzj+tSQFzHQ4202nrWdzRnQ2KLTgUUNnUvXf0BJcg==", + "license": "Apache-2.0", "dependencies": { "@react-aria/i18n": "^3.12.8", "@react-aria/live-announcer": "^3.4.2", @@ -3656,6 +4196,7 @@ "version": "3.9.8", "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz", "integrity": "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==", + "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0" }, @@ -3670,6 +4211,7 @@ "version": "3.7.2", "resolved": "https://registry.npmjs.org/@react-aria/switch/-/switch-3.7.2.tgz", "integrity": "sha512-vaREbp1gFjv+jEMXoXpNK7JYFO/jhwnSYAwEINNWnwf54IGeHvTPaB2NwolYSFvP4HAj8TKYbGFUSz7RKLhLgw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/toggle": "^3.11.2", "@react-stately/toggle": "^3.8.3", @@ -3686,6 +4228,7 @@ "version": "3.17.2", "resolved": "https://registry.npmjs.org/@react-aria/table/-/table-3.17.2.tgz", "integrity": "sha512-wsF3JqiAKcol1sfeNqTxyzH6+nxu0sAfyuh+XQfp1tvSGx15NifYeNKovNX4EPpUVkAI7jL5Le+eYeYYGELfnw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/focus": "^3.20.2", "@react-aria/grid": "^3.13.0", @@ -3712,6 +4255,7 @@ "version": "3.10.2", "resolved": "https://registry.npmjs.org/@react-aria/tabs/-/tabs-3.10.2.tgz", "integrity": "sha512-rpEgh//Gnew3le49tQVFOQ6ZyacJdaNUDXHt0ocguXb+2UrKtH54M8oIAE7E8KaB1puQlFXRs+Rjlr1rOlmjEQ==", + "license": "Apache-2.0", "dependencies": { "@react-aria/focus": "^3.20.2", "@react-aria/i18n": "^3.12.8", @@ -3731,6 +4275,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/@react-aria/tag/-/tag-3.5.2.tgz", "integrity": "sha512-xZ5Df0x+xcDg6UTDvnjP4pu+XrmYVaYcqzF7RGoCD1KyRCHU5Czg9p+888NB0K+vnJHfNsQh6rmMhDUydXu9eg==", + "license": "Apache-2.0", "dependencies": { "@react-aria/gridlist": "^3.12.0", "@react-aria/i18n": "^3.12.8", @@ -3752,6 +4297,7 @@ "version": "3.17.2", "resolved": "https://registry.npmjs.org/@react-aria/textfield/-/textfield-3.17.2.tgz", "integrity": "sha512-4KINB0HueYUHUgvi/ThTP27hu4Mv5ujG55pH3dmSRD4Olu/MRy1m/Psq72o8LTf4bTOM9ZP1rKccUg6xfaMidA==", + "license": "Apache-2.0", "dependencies": { "@react-aria/form": "^3.0.15", "@react-aria/interactions": "^3.25.0", @@ -3772,6 +4318,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/@react-aria/toast/-/toast-3.0.2.tgz", "integrity": "sha512-iaiHDE1CKYM3BbNEp3A2Ed8YAlpXUGyY6vesKISdHEZ2lJ7r+1hbcFoTNdG8HfbB8Lz5vw8Wd2o+ZmQ2tnDY9Q==", + "license": "Apache-2.0", "dependencies": { "@react-aria/i18n": "^3.12.8", "@react-aria/interactions": "^3.25.0", @@ -3791,6 +4338,7 @@ "version": "3.11.2", "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.11.2.tgz", "integrity": "sha512-JOg8yYYCjLDnEpuggPo9GyXFaT/B238d3R8i/xQ6KLelpi3fXdJuZlFD6n9NQp3DJbE8Wj+wM5/VFFAi3cISpw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/interactions": "^3.25.0", "@react-aria/utils": "^3.28.2", @@ -3808,6 +4356,7 @@ "version": "3.0.0-beta.15", "resolved": "https://registry.npmjs.org/@react-aria/toolbar/-/toolbar-3.0.0-beta.15.tgz", "integrity": "sha512-PNGpNIKIsCW8rxI9XXSADlLrSpikILJKKECyTRw9KwvXDRc44pezvdjGHCNinQcKsQoy5BtkK5cTSAyVqzzTXQ==", + "license": "Apache-2.0", "dependencies": { "@react-aria/focus": "^3.20.2", "@react-aria/i18n": "^3.12.8", @@ -3824,6 +4373,7 @@ "version": "3.8.2", "resolved": "https://registry.npmjs.org/@react-aria/tooltip/-/tooltip-3.8.2.tgz", "integrity": "sha512-ctVTgh1LXvmr1ve3ehAWfvlJR7nHYZeqhl/g1qnA+983LQtc1IF9MraCs92g0m7KpBwCihuA+aYwTPsUHfKfXg==", + "license": "Apache-2.0", "dependencies": { "@react-aria/interactions": "^3.25.0", "@react-aria/utils": "^3.28.2", @@ -3841,6 +4391,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/@react-aria/tree/-/tree-3.0.2.tgz", "integrity": "sha512-gr06Y1760+kdlDeUcGNR+PCuJMtlrdtNMGG1Z0fSygy8y7/zVdTOLQp0c1Q3pjL2nr7Unjz/H1xSgERParHsbg==", + "license": "Apache-2.0", "dependencies": { "@react-aria/gridlist": "^3.12.0", "@react-aria/i18n": "^3.12.8", @@ -3860,6 +4411,7 @@ "version": "3.28.2", "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.28.2.tgz", "integrity": "sha512-J8CcLbvnQgiBn54eeEvQQbIOfBF3A1QizxMw9P4cl9MkeR03ug7RnjTIdJY/n2p7t59kLeAB3tqiczhcj+Oi5w==", + "license": "Apache-2.0", "dependencies": { "@react-aria/ssr": "^3.9.8", "@react-stately/flags": "^3.1.1", @@ -3877,6 +4429,7 @@ "version": "3.8.22", "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.22.tgz", "integrity": "sha512-EO3R8YTKZ7HkLl9k1Y2uBKYBgpJagth4/4W7mfpJZE24A3fQnCP8zx1sweXiAm0mirR4J6tNaK7Ia8ssP5TpOw==", + "license": "Apache-2.0", "dependencies": { "@react-aria/interactions": "^3.25.0", "@react-aria/utils": "^3.28.2", @@ -3892,6 +4445,7 @@ "version": "3.8.0", "resolved": "https://registry.npmjs.org/@react-stately/calendar/-/calendar-3.8.0.tgz", "integrity": "sha512-YAuJiR9EtVThX91gU2ay/6YgPe0LvZWEssu4BS0Atnwk5cAo32gvF5FMta9ztH1LIULdZFaypU/C1mvnayMf+Q==", + "license": "Apache-2.0", "dependencies": { "@internationalized/date": "^3.8.0", "@react-stately/utils": "^3.10.6", @@ -3907,6 +4461,7 @@ "version": "3.6.13", "resolved": "https://registry.npmjs.org/@react-stately/checkbox/-/checkbox-3.6.13.tgz", "integrity": "sha512-b8+bkOhobzuJ5bAA16JpYg1tM973eNXD3U4h/8+dckLndKHRjIwPvrL25tzKN7NcQp2LKVCauFesgI+Z+/2FJg==", + "license": "Apache-2.0", "dependencies": { "@react-stately/form": "^3.1.3", "@react-stately/utils": "^3.10.6", @@ -3922,6 +4477,7 @@ "version": "3.12.3", "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.3.tgz", "integrity": "sha512-QfSBME2QWDjUw/RmmUjrYl/j1iCYcYCIDsgZda1OeRtt63R11k0aqmmwrDRwCsA+Sv+D5QgkOp4KK+CokTzoVQ==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0", "@swc/helpers": "^0.5.0" @@ -3934,6 +4490,7 @@ "version": "3.8.4", "resolved": "https://registry.npmjs.org/@react-stately/color/-/color-3.8.4.tgz", "integrity": "sha512-LXmfnJPWnL5q1/Z8Pn2d+9efrClLWCiK6c3IGXN8ZWcdR/cMJ/w9SY9f7evyXvmeUmdU1FTGgoSVqGfup3tSyA==", + "license": "Apache-2.0", "dependencies": { "@internationalized/number": "^3.6.1", "@internationalized/string": "^3.2.6", @@ -3953,6 +4510,7 @@ "version": "3.10.4", "resolved": "https://registry.npmjs.org/@react-stately/combobox/-/combobox-3.10.4.tgz", "integrity": "sha512-sgujLhukIGKskLDrOL4SAbO7WOgLsD7gSdjRQZ0f/e8bWMmUOWEp22T+X1hMMcuVRkRdXlEF1kH2/E6BVanXYw==", + "license": "Apache-2.0", "dependencies": { "@react-stately/collections": "^3.12.3", "@react-stately/form": "^3.1.3", @@ -3972,6 +4530,7 @@ "version": "3.14.0", "resolved": "https://registry.npmjs.org/@react-stately/datepicker/-/datepicker-3.14.0.tgz", "integrity": "sha512-JSkQfKW0+WpPQyOOeRPBLwXkVfpTUwgZJDnHBCud5kEuQiFFyeAIbL57RNXc4AX2pzY3piQa6OHnjDGTfqClxQ==", + "license": "Apache-2.0", "dependencies": { "@internationalized/date": "^3.8.0", "@internationalized/string": "^3.2.6", @@ -3990,6 +4549,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@react-stately/disclosure/-/disclosure-3.0.3.tgz", "integrity": "sha512-4kB+WDXVcrxCmJ+X6c23wa5Ax5dPSpm6Ef8DktLrLcUfJyfr+SWs5/IfkrYG0sOl3/u5OwyWe1pq3hDpzyDlLA==", + "license": "Apache-2.0", "dependencies": { "@react-stately/utils": "^3.10.6", "@react-types/shared": "^3.29.0", @@ -4003,6 +4563,7 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/@react-stately/dnd/-/dnd-3.5.3.tgz", "integrity": "sha512-e4IodPF7fv9hR6jqSjiyrrFQ/6NbHNM5Ft1MJzCu6tJHvT+sl6qxIP5A+XR3wkjMpi4QW2WhVUmoFNbS/6ZAug==", + "license": "Apache-2.0", "dependencies": { "@react-stately/selection": "^3.20.1", "@react-types/shared": "^3.29.0", @@ -4016,6 +4577,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.1.tgz", "integrity": "sha512-XPR5gi5LfrPdhxZzdIlJDz/B5cBf63l4q6/AzNqVWFKgd0QqY5LvWJftXkklaIUpKSJkIKQb8dphuZXDtkWNqg==", + "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0" } @@ -4024,6 +4586,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/@react-stately/form/-/form-3.1.3.tgz", "integrity": "sha512-Jisgm0facSS3sAzHfSgshoCo3LxfO0wmQj98MOBCGXyVL+MSwx2ilb38eXIyBCzHJzJnPRTLaK/E4T49aph47A==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0", "@swc/helpers": "^0.5.0" @@ -4036,6 +4599,7 @@ "version": "3.11.1", "resolved": "https://registry.npmjs.org/@react-stately/grid/-/grid-3.11.1.tgz", "integrity": "sha512-xMk2YsaIKkF8dInRLUFpUXBIqnYt88hehhq2nb65RFgsFFhngE/OkaFudSUzaYPc1KvHpW+oHqvseC+G1iDG2w==", + "license": "Apache-2.0", "dependencies": { "@react-stately/collections": "^3.12.3", "@react-stately/selection": "^3.20.1", @@ -4051,6 +4615,7 @@ "version": "3.12.1", "resolved": "https://registry.npmjs.org/@react-stately/list/-/list-3.12.1.tgz", "integrity": "sha512-N+YCInNZ2OpY0WUNvJWUTyFHtzE5yBtZ9DI4EHJDvm61+jmZ2s3HszOfa7j+7VOKq78VW3m5laqsQNWvMrLFrQ==", + "license": "Apache-2.0", "dependencies": { "@react-stately/collections": "^3.12.3", "@react-stately/selection": "^3.20.1", @@ -4066,6 +4631,7 @@ "version": "3.9.3", "resolved": "https://registry.npmjs.org/@react-stately/menu/-/menu-3.9.3.tgz", "integrity": "sha512-9x1sTX3Xq2Q3mJUHV+YN9MR36qNzgn8eBSLa40eaFDaOOtoJ+V10m7OriUfpjey7WzLBpq00Sfda54/PbQHZ0g==", + "license": "Apache-2.0", "dependencies": { "@react-stately/overlays": "^3.6.15", "@react-types/menu": "^3.10.0", @@ -4080,6 +4646,7 @@ "version": "3.9.11", "resolved": "https://registry.npmjs.org/@react-stately/numberfield/-/numberfield-3.9.11.tgz", "integrity": "sha512-gAFSZIHnZsgIWVPgGRUUpfW6zM7TCV5oS1SCY90ay5nrS7JCXurQbMrWJLOWHTdM5iSeYMgoyt68OK5KD0KHMw==", + "license": "Apache-2.0", "dependencies": { "@internationalized/number": "^3.6.1", "@react-stately/form": "^3.1.3", @@ -4095,6 +4662,7 @@ "version": "3.6.15", "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.15.tgz", "integrity": "sha512-LBaGpXuI+SSd5HSGzyGJA0Gy09V2tl2G/r0lllTYqwt0RDZR6p7IrhdGVXZm6vI0oWEnih7yLC32krkVQrffgQ==", + "license": "Apache-2.0", "dependencies": { "@react-stately/utils": "^3.10.6", "@react-types/overlays": "^3.8.14", @@ -4108,6 +4676,7 @@ "version": "3.10.12", "resolved": "https://registry.npmjs.org/@react-stately/radio/-/radio-3.10.12.tgz", "integrity": "sha512-hFH45CXVa7uyXeTYQy7LGR0SnmGnNRx7XnEXS25w4Ch6BpH8m8SAbhKXqysgcmsE3xrhRas7P9zWw7wI24G28Q==", + "license": "Apache-2.0", "dependencies": { "@react-stately/form": "^3.1.3", "@react-stately/utils": "^3.10.6", @@ -4123,6 +4692,7 @@ "version": "3.5.11", "resolved": "https://registry.npmjs.org/@react-stately/searchfield/-/searchfield-3.5.11.tgz", "integrity": "sha512-vOgK3kgkYcyjTLsBABVzoQL9w6qBamnWAQICcw5OkA6octnF7NZ5DqdjkwnMY95KOGchiTlD5tNNHrz0ekeGiw==", + "license": "Apache-2.0", "dependencies": { "@react-stately/utils": "^3.10.6", "@react-types/searchfield": "^3.6.1", @@ -4136,6 +4706,7 @@ "version": "3.6.12", "resolved": "https://registry.npmjs.org/@react-stately/select/-/select-3.6.12.tgz", "integrity": "sha512-5o/NAaENO/Gxs1yui5BHLItxLnDPSQJ5HDKycuD0/gGC17BboAGEY/F9masiQ5qwRPe3JEc0QfvMRq3yZVNXog==", + "license": "Apache-2.0", "dependencies": { "@react-stately/form": "^3.1.3", "@react-stately/list": "^3.12.1", @@ -4152,6 +4723,7 @@ "version": "3.20.1", "resolved": "https://registry.npmjs.org/@react-stately/selection/-/selection-3.20.1.tgz", "integrity": "sha512-K9MP6Rfg2yvFoY2Cr+ykA7bP4EBXlGaq5Dqfa1krvcXlEgMbQka5muLHdNXqjzGgcwPmS1dx1NECD15q63NtOw==", + "license": "Apache-2.0", "dependencies": { "@react-stately/collections": "^3.12.3", "@react-stately/utils": "^3.10.6", @@ -4166,6 +4738,7 @@ "version": "3.6.3", "resolved": "https://registry.npmjs.org/@react-stately/slider/-/slider-3.6.3.tgz", "integrity": "sha512-755X1jhpRD1bqf/5Ax1xuSpZbnG/0EEHGOowH28FLYKy5+1l4QVDGPFYxLB9KzXPdRAr9EF0j2kRhH2d8MCksQ==", + "license": "Apache-2.0", "dependencies": { "@react-stately/utils": "^3.10.6", "@react-types/shared": "^3.29.0", @@ -4180,6 +4753,7 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/@react-stately/table/-/table-3.14.1.tgz", "integrity": "sha512-7P5h4YBAv3B/7BGq/kln+xSKgJCSq4xjt4HmJA7ZkGnEksUPUokBNQdWwZsy3lX/mwunaaKR9x/YNIu7yXB02g==", + "license": "Apache-2.0", "dependencies": { "@react-stately/collections": "^3.12.3", "@react-stately/flags": "^3.1.1", @@ -4199,6 +4773,7 @@ "version": "3.8.1", "resolved": "https://registry.npmjs.org/@react-stately/tabs/-/tabs-3.8.1.tgz", "integrity": "sha512-1TBbt2BXbemstb/gEYw/NVt3esi5WvgWQW5Z7G8nDzLkpnMHOZXueoUkMxsdm0vhE8p0M9fsJQCMXKvCG3JzJg==", + "license": "Apache-2.0", "dependencies": { "@react-stately/list": "^3.12.1", "@react-types/shared": "^3.29.0", @@ -4213,6 +4788,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@react-stately/toast/-/toast-3.1.0.tgz", "integrity": "sha512-9W2+evz+EARrjkR1QPLlOL5lcNpVo6PjMAIygRSaCPJ6ftQAZ6B+7xTFGPFabWh83gwXQDUgoSwC3/vosvxZaQ==", + "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0", "use-sync-external-store": "^1.4.0" @@ -4225,6 +4801,7 @@ "version": "3.8.3", "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.8.3.tgz", "integrity": "sha512-4T2V3P1RK4zEFz4vJjUXUXyB0g4Slm6stE6Ry20fzDWjltuW42cD2lmrd7ccTO/CXFmHLECcXQLD4GEbOj0epA==", + "license": "Apache-2.0", "dependencies": { "@react-stately/utils": "^3.10.6", "@react-types/checkbox": "^3.9.3", @@ -4239,6 +4816,7 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/@react-stately/tooltip/-/tooltip-3.5.3.tgz", "integrity": "sha512-btfy/gQ3Eccudx//4HkyQ+CRr3vxbLs74HYHthaoJ9GZbRj/3XDzfUM2X16zRoqTZVrIz/AkUj7AfGfsitU5nQ==", + "license": "Apache-2.0", "dependencies": { "@react-stately/overlays": "^3.6.15", "@react-types/tooltip": "^3.4.16", @@ -4252,6 +4830,7 @@ "version": "3.8.9", "resolved": "https://registry.npmjs.org/@react-stately/tree/-/tree-3.8.9.tgz", "integrity": "sha512-j/LLI9UvbqcfOdl2v9m3gET3etUxoQzv3XdryNAbSkg0jTx8/13Fgi/Xp98bUcNLfynfeGW5P/fieU71sMkGog==", + "license": "Apache-2.0", "dependencies": { "@react-stately/collections": "^3.12.3", "@react-stately/selection": "^3.20.1", @@ -4267,6 +4846,7 @@ "version": "3.10.6", "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.6.tgz", "integrity": "sha512-O76ip4InfTTzAJrg8OaZxKU4vvjMDOpfA/PGNOytiXwBbkct2ZeZwaimJ8Bt9W1bj5VsZ81/o/tW4BacbdDOMA==", + "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0" }, @@ -4278,6 +4858,7 @@ "version": "3.7.12", "resolved": "https://registry.npmjs.org/@react-types/breadcrumbs/-/breadcrumbs-3.7.12.tgz", "integrity": "sha512-+LvGEADlv11mLQjxEAZriptSYJJTP+2OIFEKx0z9mmpp+8jTlEHFhAnRVaE6I9QCxcDB5F6q/olfizSwOPOMIg==", + "license": "Apache-2.0", "dependencies": { "@react-types/link": "^3.6.0", "@react-types/shared": "^3.29.0" @@ -4290,6 +4871,7 @@ "version": "3.12.0", "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.12.0.tgz", "integrity": "sha512-YrASNa+RqGQpzJcxNAahzNuTYVID1OE6HCorrEOXIyGS3EGogHsQmFs9OyThXnGHq6q4rLlA806/jWbP9uZdxA==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4301,6 +4883,7 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/@react-types/calendar/-/calendar-3.7.0.tgz", "integrity": "sha512-RiEfX2ZTcvfRktQc5obOJtNTgW+UwjNOUW5yf9CLCNOSM07e0w5jtC1ewsOZZbcctMrMCljjL8niGWiBv1wQ1Q==", + "license": "Apache-2.0", "dependencies": { "@internationalized/date": "^3.8.0", "@react-types/shared": "^3.29.0" @@ -4313,6 +4896,7 @@ "version": "3.9.3", "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.9.3.tgz", "integrity": "sha512-h6wmK7CraKHKE6L13Ut+CtnjRktbMRhkCSorv7eg82M6p4PDhZ7mfDSh13IlGR4sryT8Ka+aOjOU+EvMrKiduA==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4324,6 +4908,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@react-types/color/-/color-3.0.4.tgz", "integrity": "sha512-D6Uea8kYGaoZRHgemJ0b0+iXbrvABP8RzsctL8Yp5QVyGgYJDMO8/7eZ3tdtGs/V8Iv+yCzG4yBexPA95i6tEg==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0", "@react-types/slider": "^3.7.10" @@ -4336,6 +4921,7 @@ "version": "3.13.4", "resolved": "https://registry.npmjs.org/@react-types/combobox/-/combobox-3.13.4.tgz", "integrity": "sha512-4mX7eZ/Bv3YWzEzLEZAF/TfKM+I+SCsvnm/cHqOJq3jEE8aVU1ql4Q1+3+SvciX3pfFIfeKlu9S3oYKRT5WIgg==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4347,6 +4933,7 @@ "version": "3.12.0", "resolved": "https://registry.npmjs.org/@react-types/datepicker/-/datepicker-3.12.0.tgz", "integrity": "sha512-dw/xflOdQPQ3uEABaBrZRTvjsMRu5/VZjRx9ygc64sX2N7HKIt+foMPXKJ+1jhtki2p4gigNVjcnJndJHoj9SA==", + "license": "Apache-2.0", "dependencies": { "@internationalized/date": "^3.8.0", "@react-types/calendar": "^3.7.0", @@ -4361,6 +4948,7 @@ "version": "3.5.17", "resolved": "https://registry.npmjs.org/@react-types/dialog/-/dialog-3.5.17.tgz", "integrity": "sha512-rKe2WrT272xuCH13euegBGjJAORYXJpHsX2hlu/f02TmMG4nSLss9vKBnY2N7k7nci65k5wDTW6lcsvQ4Co5zQ==", + "license": "Apache-2.0", "dependencies": { "@react-types/overlays": "^3.8.14", "@react-types/shared": "^3.29.0" @@ -4373,6 +4961,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.3.1.tgz", "integrity": "sha512-bPDckheJiHSIzSeSkLqrO6rXRLWvciFJr9rpCjq/+wBj6HsLh2iMpkB/SqmRHTGpPlJvlu0b7AlxK1FYE0QSKA==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4384,6 +4973,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/@react-types/link/-/link-3.6.0.tgz", "integrity": "sha512-BQ5Tktb+fUxvtqksAJZuP8Z/bpmnQ/Y/zgwxfU0OKmIWkKMUsXY+e0GBVxwFxeh39D77stpVxRsTl7NQrjgtSw==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4395,6 +4985,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/@react-types/listbox/-/listbox-3.6.0.tgz", "integrity": "sha512-+1ugDKTxson/WNOQZO4BfrnQ6cGDt+72mEytXMsSsd4aEC+x3RyUv6NKwdOl4n602cOreo0MHtap1X2BOACVoQ==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4406,6 +4997,7 @@ "version": "3.10.0", "resolved": "https://registry.npmjs.org/@react-types/menu/-/menu-3.10.0.tgz", "integrity": "sha512-DKMqEmUmarVCK0jblNkSlzSH53AAsxWCX9RaKZeP9EnRs2/l1oZRuiQVHlOQRgYwEigAXa2TrwcX4nnxZ+U36Q==", + "license": "Apache-2.0", "dependencies": { "@react-types/overlays": "^3.8.14", "@react-types/shared": "^3.29.0" @@ -4418,6 +5010,7 @@ "version": "3.4.8", "resolved": "https://registry.npmjs.org/@react-types/meter/-/meter-3.4.8.tgz", "integrity": "sha512-uXmHdUDbAo7L3EkytrUrU6DLOFUt63s9QSTcDp+vwyWoshY4/4Dm4JARdmhJU2ZP1nb2Sy45ASeMvSBw3ia2oA==", + "license": "Apache-2.0", "dependencies": { "@react-types/progress": "^3.5.11" }, @@ -4429,6 +5022,7 @@ "version": "3.8.10", "resolved": "https://registry.npmjs.org/@react-types/numberfield/-/numberfield-3.8.10.tgz", "integrity": "sha512-mdb4lMC4skO8Eqd0GeU4lJgDTEvqIhtINB5WCzLVZFrFVuxgWDoU5otsu0lbWhCnUA7XWQxupGI//TC1LLppjQ==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4440,6 +5034,7 @@ "version": "3.8.14", "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.8.14.tgz", "integrity": "sha512-XJS67KHYhdMvPNHXNGdmc85gE+29QT5TwC58V4kxxHVtQh9fYzEEPzIV8K84XWSz04rRGe3fjDgRNbcqBektWQ==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4451,6 +5046,7 @@ "version": "3.5.11", "resolved": "https://registry.npmjs.org/@react-types/progress/-/progress-3.5.11.tgz", "integrity": "sha512-CysuMld/lycOckrnlvrlsVoJysDPeBnUYBChwtqwiv4ZNRXos+wgAL1ows6dl7Nr57/FH5B4v5gf9AHEo7jUvw==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4462,6 +5058,7 @@ "version": "3.8.8", "resolved": "https://registry.npmjs.org/@react-types/radio/-/radio-3.8.8.tgz", "integrity": "sha512-QfAIp+0CnRSnoRTJVXUEPi+9AvFvRzWLIKEnE9OmgXjuvJCU3QNiwd8NWjNeE+94QBEVvAZQcqGU+44q5poxNg==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4473,6 +5070,7 @@ "version": "3.6.1", "resolved": "https://registry.npmjs.org/@react-types/searchfield/-/searchfield-3.6.1.tgz", "integrity": "sha512-XR4tYktxHxGJufpO0MTAPknIbmN5eZqXCZwTdBS4tecihf9iGDsXmrBOs+M7LEnil67GaZcFrMhKxOMVpLwZAg==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0", "@react-types/textfield": "^3.12.1" @@ -4485,6 +5083,7 @@ "version": "3.9.11", "resolved": "https://registry.npmjs.org/@react-types/select/-/select-3.9.11.tgz", "integrity": "sha512-uEpQCgDlrq/5fW05FgNEsqsqpvZVKfHQO9Mp7OTqGtm4UBNAbcQ6hOV7MJwQCS25Lu2luzOYdgqDUN8eAATJVQ==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4496,6 +5095,7 @@ "version": "3.29.0", "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.29.0.tgz", "integrity": "sha512-IDQYu/AHgZimObzCFdNl1LpZvQW/xcfLt3v20sorl5qRucDVj4S9os98sVTZ4IRIBjmS+MkjqpR5E70xan7ooA==", + "license": "Apache-2.0", "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } @@ -4504,6 +5104,7 @@ "version": "3.7.10", "resolved": "https://registry.npmjs.org/@react-types/slider/-/slider-3.7.10.tgz", "integrity": "sha512-Yb8wbpu2gS7AwvJUuz0IdZBRi6eIBZq32BSss4UHX0StA8dtR1/K4JeTsArxwiA3P0BA6t0gbR6wzxCvVA9fRw==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4515,6 +5116,7 @@ "version": "3.5.10", "resolved": "https://registry.npmjs.org/@react-types/switch/-/switch-3.5.10.tgz", "integrity": "sha512-YyNhx4CvuJ0Rvv7yMuQaqQuOIeg+NwLV00NHHJ+K0xEANSLcICLOLPNMOqRIqLSQDz5vDI705UKk8gVcxqPX5g==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4526,6 +5128,7 @@ "version": "3.12.0", "resolved": "https://registry.npmjs.org/@react-types/table/-/table-3.12.0.tgz", "integrity": "sha512-dmTzjCYwHf2HBOeTa/CEL177Aox0f0mkeLF5nQw/2z6SBolfmYoAwVTPxTaYFVu4MkEJxQTz9AuAsJvCbRJbhg==", + "license": "Apache-2.0", "dependencies": { "@react-types/grid": "^3.3.1", "@react-types/shared": "^3.29.0" @@ -4538,6 +5141,7 @@ "version": "3.3.14", "resolved": "https://registry.npmjs.org/@react-types/tabs/-/tabs-3.3.14.tgz", "integrity": "sha512-/uKsA7L2dctKU0JEaBWerlX+3BoXpKUFr3kHpRUoH66DSGvAo34vZ7kv/BHMZifJenIbF04GhDBsGp1zjrQKBg==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4549,6 +5153,7 @@ "version": "3.12.1", "resolved": "https://registry.npmjs.org/@react-types/textfield/-/textfield-3.12.1.tgz", "integrity": "sha512-6YTAMCKjEGuXg0A4bZA77j5QJ1a6yFviMUWsCIL6Dxq5K3TklzVsbAduSbHomPPuvkNTBSW4+TUJrVSnoTjMNA==", + "license": "Apache-2.0", "dependencies": { "@react-types/shared": "^3.29.0" }, @@ -4560,6 +5165,7 @@ "version": "3.4.16", "resolved": "https://registry.npmjs.org/@react-types/tooltip/-/tooltip-3.4.16.tgz", "integrity": "sha512-XEyKeqR3YxqJcR0cpigLGEBeRTEzrB0cu++IaADdqXJ8dBzS6s8y9EgR5UvKZmX1CQOBvMfXyYkj7nmJ039fOw==", + "license": "Apache-2.0", "dependencies": { "@react-types/overlays": "^3.8.14", "@react-types/shared": "^3.29.0" @@ -4572,6 +5178,7 @@ "version": "5.0.5", "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", + "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.0.1", "estree-walker": "^2.0.2", @@ -4592,12 +5199,14 @@ "node_modules/@rollup/plugin-inject/node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" }, "node_modules/@rollup/pluginutils": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", @@ -4618,12 +5227,14 @@ "node_modules/@rollup/pluginutils/node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" }, "node_modules/@rollup/pluginutils/node_modules/picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -4631,6 +5242,201 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz", + "integrity": "sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.2.tgz", + "integrity": "sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.2.tgz", + "integrity": "sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.2.tgz", + "integrity": "sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.2.tgz", + "integrity": "sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.2.tgz", + "integrity": "sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.2.tgz", + "integrity": "sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.2.tgz", + "integrity": "sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.2.tgz", + "integrity": "sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.2.tgz", + "integrity": "sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.2.tgz", + "integrity": "sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.2.tgz", + "integrity": "sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.2.tgz", + "integrity": "sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.2.tgz", + "integrity": "sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.2.tgz", + "integrity": "sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.40.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.2.tgz", @@ -4638,6 +5444,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -4650,11 +5457,51 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.2.tgz", + "integrity": "sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.2.tgz", + "integrity": "sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.2.tgz", + "integrity": "sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -4665,6 +5512,7 @@ "version": "0.5.17", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" } @@ -4824,6 +5672,7 @@ "version": "3.13.2", "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.2.tgz", "integrity": "sha512-LceSUgABBKF6HSsHK2ZqHzQ37IKV/jlaWbHm+NyTa3/WNb/JZVcThDuTainf+PixltOOcFCYXwxbLpOX9sCx+g==", + "license": "MIT", "dependencies": { "@tanstack/virtual-core": "3.13.2" }, @@ -4840,6 +5689,7 @@ "version": "3.13.2", "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.2.tgz", "integrity": "sha512-Qzz4EgzMbO5gKrmqUondCjiHcuu4B1ftHb0pjCut661lXZdGoHeze9f/M8iwsK1t5LGR6aNuNGU7mxkowaW6RQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -4849,6 +5699,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/along/-/along-6.5.0.tgz", "integrity": "sha512-LLyWQ0AARqJCmMcIEAXF4GEu8usmd4Kbz3qk1Oy5HoRNpZX47+i5exQtmIWKdqJ1MMhW26fCTXgpsEs5zgJ5gw==", + "license": "MIT", "dependencies": { "@turf/bearing": "^6.5.0", "@turf/destination": "^6.5.0", @@ -4864,6 +5715,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.5.0.tgz", "integrity": "sha512-xCZdiuojokLbQ+29qR6qoMD89hv+JAgWjLrwSEWL+3JV8IXKeNFl6XkEJz9HGkVpnXvQKJoRz4/liT+8ZZ5Jyg==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/meta": "^6.5.0" @@ -4876,6 +5728,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.5.0.tgz", "integrity": "sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/meta": "^6.5.0" @@ -4888,6 +5741,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/bbox-polygon/-/bbox-polygon-6.5.0.tgz", "integrity": "sha512-+/r0NyL1lOG3zKZmmf6L8ommU07HliP4dgYToMoTxqzsWzyLjaj/OzgQ8rBmv703WJX+aS6yCmLuIhYqyufyuw==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0" }, @@ -4899,6 +5753,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-6.5.0.tgz", "integrity": "sha512-dxINYhIEMzgDOztyMZc20I7ssYVNEpSv04VbMo5YPQsqa80KO3TFvbuCahMsCAW5z8Tncc8dwBlEFrmRjJG33A==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" @@ -4911,6 +5766,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-6.5.0.tgz", "integrity": "sha512-45+C7LC5RMbRWrxh3Z0Eihsc8db1VGBO5d9BLTOAwU4jR6SgsunTfRWR16X7JUwIDYlCVEmnjcXJNi/kIU3VIw==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" @@ -4923,6 +5779,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.5.0.tgz", "integrity": "sha512-DtSuVFB26SI+hj0SjrvXowGTUCHlgevPAIsukssW6BG5MlNSBQAo70wpICBNJL6RjukXg8d2eXaAWuD/CqL00A==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" @@ -4935,6 +5792,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/buffer/-/buffer-6.5.0.tgz", "integrity": "sha512-qeX4N6+PPWbKqp1AVkBVWFerGjMYMUyencwfnkCesoznU6qvfugFHNAngNqIBVnJjZ5n8IFyOf+akcxnrt9sNg==", + "license": "MIT", "dependencies": { "@turf/bbox": "^6.5.0", "@turf/center": "^6.5.0", @@ -4952,6 +5810,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/center/-/center-6.5.0.tgz", "integrity": "sha512-T8KtMTfSATWcAX088rEDKjyvQCBkUsLnK/Txb6/8WUXIeOZyHu42G7MkdkHRoHtwieLdduDdmPLFyTdG5/e7ZQ==", + "license": "MIT", "dependencies": { "@turf/bbox": "^6.5.0", "@turf/helpers": "^6.5.0" @@ -4964,6 +5823,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-6.5.0.tgz", "integrity": "sha512-MwE1oq5E3isewPprEClbfU5pXljIK/GUOMbn22UM3IFPDJX0KeoyLNwghszkdmFp/qMGL/M13MMWvU+GNLXP/A==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/meta": "^6.5.0" @@ -4976,6 +5836,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-6.5.0.tgz", "integrity": "sha512-oU1+Kq9DgRnoSbWFHKnnUdTmtcRUMmHoV9DjTXu9vOLNV5OWtAAh1VZ+mzsioGGzoDNT/V5igbFOkMfBQc0B6A==", + "license": "MIT", "dependencies": { "@turf/destination": "^6.5.0", "@turf/helpers": "^6.5.0" @@ -4985,39 +5846,36 @@ } }, "node_modules/@turf/clean-coords": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-7.2.0.tgz", - "integrity": "sha512-+5+J1+D7wW7O/RDXn46IfCHuX1gIV1pIAQNSA7lcDbr3HQITZj334C4mOGZLEcGbsiXtlHWZiBtm785Vg8i+QQ==", + "version": "7.1.0", + "license": "MIT", "dependencies": { - "@turf/helpers": "^7.2.0", - "@turf/invariant": "^7.2.0", + "@turf/helpers": "^7.1.0", + "@turf/invariant": "^7.1.0", "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" + "tslib": "^2.6.2" }, "funding": { "url": "https://opencollective.com/turf" } }, "node_modules/@turf/clean-coords/node_modules/@turf/helpers": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.2.0.tgz", - "integrity": "sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==", + "version": "7.1.0", + "license": "MIT", "dependencies": { "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" + "tslib": "^2.6.2" }, "funding": { "url": "https://opencollective.com/turf" } }, "node_modules/@turf/clean-coords/node_modules/@turf/invariant": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-7.2.0.tgz", - "integrity": "sha512-kV4u8e7Gkpq+kPbAKNC21CmyrXzlbBgFjO1PhrHPgEdNqXqDawoZ3i6ivE3ULJj2rSesCjduUaC/wyvH/sNr2Q==", + "version": "7.1.0", + "license": "MIT", "dependencies": { - "@turf/helpers": "^7.2.0", + "@turf/helpers": "^7.1.0", "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" + "tslib": "^2.6.2" }, "funding": { "url": "https://opencollective.com/turf" @@ -5027,6 +5885,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.5.0.tgz", "integrity": "sha512-mzVtTFj/QycXOn6ig+annKrM6ZlimreKYz6f/GSERytOpgzodbQyOgkfwru100O1KQhhjSudKK4DsQ0oyi9cTw==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0" }, @@ -5038,6 +5897,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-6.5.0.tgz", "integrity": "sha512-4cnWQlNC8d1tItOz9B4pmJdWpXqS0vEvv65bI/Pj/genJnsL7evI0/Xw42RvEGROS481MPiU80xzvwxEvhQiMQ==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" @@ -5050,6 +5910,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-6.5.0.tgz", "integrity": "sha512-l8iR5uJqvI+5Fs6leNbhPY5t/a3vipUF/3AeVLpwPQcgmedNXyheYuy07PcMGH5Jdpi5gItOiTqwiU/bUH4b3A==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0", @@ -5063,6 +5924,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.5.0.tgz", "integrity": "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" @@ -5075,6 +5937,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/ellipse/-/ellipse-6.5.0.tgz", "integrity": "sha512-kuXtwFviw/JqnyJXF1mrR/cb496zDTSbGKtSiolWMNImYzGGkbsAsFTjwJYgD7+4FixHjp0uQPzo70KDf3AIBw==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0", @@ -5089,6 +5952,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz", "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==", + "license": "MIT", "funding": { "url": "https://opencollective.com/turf" } @@ -5097,6 +5961,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-6.5.0.tgz", "integrity": "sha512-2legGJeKrfFkzntcd4GouPugoqPUjexPZnOvfez+3SfIMrHvulw8qV8u7pfVyn2Yqs53yoVCEjS5sEpvQ5YRQg==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0", @@ -5110,6 +5975,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz", "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0" }, @@ -5121,6 +5987,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-6.5.0.tgz", "integrity": "sha512-CS6R1tZvVQD390G9Ea4pmpM6mJGPWoL82jD46y0q1KSor9s6HupMIo1kY4Ny+AEYQl9jd21V3Scz20eldpbTVA==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0", @@ -5136,6 +6003,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-6.5.0.tgz", "integrity": "sha512-jI625Ho4jSuJESNq66Mmi290ZJ5pPZiQZruPVpmHkUw257Pew0alMmb6YrqYNnLUuiVVONxAAKXUVeeUGtycfw==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0", @@ -5149,6 +6017,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz", "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0" }, @@ -5160,6 +6029,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/midpoint/-/midpoint-6.5.0.tgz", "integrity": "sha512-MyTzV44IwmVI6ec9fB2OgZ53JGNlgOpaYl9ArKoF49rXpL84F9rNATndbe0+MQIhdkw8IlzA6xVP4lZzfMNVCw==", + "license": "MIT", "dependencies": { "@turf/bearing": "^6.5.0", "@turf/destination": "^6.5.0", @@ -5174,6 +6044,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-6.5.0.tgz", "integrity": "sha512-WthrvddddvmymnC+Vf7BrkHGbDOUu6Z3/6bFYUGv1kxw8tiZ6n83/VG6kHz4poHOfS0RaNflzXSkmCi64fLBlg==", + "license": "MIT", "dependencies": { "@turf/bearing": "^6.5.0", "@turf/destination": "^6.5.0", @@ -5191,6 +6062,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-6.5.0.tgz", "integrity": "sha512-opHVQ4vjUhNBly1bob6RWy+F+hsZDH9SA0UW36pIRzfpu27qipU18xup0XXEePfY6+wvhF6yL/WgCO2IbrLqEA==", + "license": "MIT", "dependencies": { "@turf/bearing": "^6.5.0", "@turf/distance": "^6.5.0", @@ -5209,6 +6081,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-6.5.0.tgz", "integrity": "sha512-5p4n/ij97EIttAq+ewSnKt0ruvuM+LIDzuczSzuHTpq4oS7Oq8yqg5TQ4nzMVuK41r/tALCk7nAoBuw3Su4Gcw==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" @@ -5221,6 +6094,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-6.5.0.tgz", "integrity": "sha512-/Pgh9mDvQWWu8HRxqpM+tKz8OzgauV+DiOcr3FCjD6ubDnrrmMJlsf6fFJmggw93mtVPrZRL6yyi9aYCQBOIvg==", + "license": "MIT", "dependencies": { "@turf/clone": "^6.5.0", "@turf/helpers": "^6.5.0", @@ -5234,6 +6108,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-6.5.0.tgz", "integrity": "sha512-IoUAMcHWotBWYwSYuYypw/LlqZmO+wcBpn8ysrBNbazkFNkLf3btSDZMkKJO/bvOzl55imr/Xj4fi3DdsLsbzQ==", + "license": "MIT", "dependencies": { "@turf/boolean-clockwise": "^6.5.0", "@turf/clone": "^6.5.0", @@ -5249,6 +6124,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-6.5.0.tgz", "integrity": "sha512-jMyqiMRK4hzREjQmnLXmkJ+VTNTx1ii8vuqRwJPcTlKbNWfjDz/5JqJlb5NaFDcdMpftWovkW5GevfnuzHnOYA==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" @@ -5261,6 +6137,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/rhumb-destination/-/rhumb-destination-6.5.0.tgz", "integrity": "sha512-RHNP1Oy+7xTTdRrTt375jOZeHceFbjwohPHlr9Hf68VdHHPMAWgAKqiX2YgSWDcvECVmiGaBKWus1Df+N7eE4Q==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" @@ -5273,6 +6150,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-6.5.0.tgz", "integrity": "sha512-oKp8KFE8E4huC2Z1a1KNcFwjVOqa99isxNOwfo4g3SUABQ6NezjKDDrnvC4yI5YZ3/huDjULLBvhed45xdCrzg==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" @@ -5282,52 +6160,48 @@ } }, "node_modules/@turf/simplify": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-7.2.0.tgz", - "integrity": "sha512-9YHIfSc8BXQfi5IvEMbCeQYqNch0UawIGwbboJaoV8rodhtk6kKV2wrpXdGqk/6Thg6/RWvChJFKVVTjVrULyQ==", + "version": "7.1.0", + "license": "MIT", "dependencies": { - "@turf/clean-coords": "^7.2.0", - "@turf/clone": "^7.2.0", - "@turf/helpers": "^7.2.0", - "@turf/meta": "^7.2.0", + "@turf/clean-coords": "^7.1.0", + "@turf/clone": "^7.1.0", + "@turf/helpers": "^7.1.0", + "@turf/meta": "^7.1.0", "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" + "tslib": "^2.6.2" }, "funding": { "url": "https://opencollective.com/turf" } }, "node_modules/@turf/simplify/node_modules/@turf/clone": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-7.2.0.tgz", - "integrity": "sha512-JlGUT+/5qoU5jqZmf6NMFIoLDY3O7jKd53Up+zbpJ2vzUp6QdwdNzwrsCeONhynWM13F0MVtPXH4AtdkrgFk4g==", + "version": "7.1.0", + "license": "MIT", "dependencies": { - "@turf/helpers": "^7.2.0", + "@turf/helpers": "^7.1.0", "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" + "tslib": "^2.6.2" }, "funding": { "url": "https://opencollective.com/turf" } }, "node_modules/@turf/simplify/node_modules/@turf/helpers": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.2.0.tgz", - "integrity": "sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==", + "version": "7.1.0", + "license": "MIT", "dependencies": { "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" + "tslib": "^2.6.2" }, "funding": { "url": "https://opencollective.com/turf" } }, "node_modules/@turf/simplify/node_modules/@turf/meta": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.2.0.tgz", - "integrity": "sha512-igzTdHsQc8TV1RhPuOLVo74Px/hyPrVgVOTgjWQZzt3J9BVseCdpfY/0cJBdlSRI4S/yTmmHl7gAqjhpYH5Yaw==", + "version": "7.1.0", + "license": "MIT", "dependencies": { - "@turf/helpers": "^7.2.0", + "@turf/helpers": "^7.1.0", "@types/geojson": "^7946.0.10" }, "funding": { @@ -5338,6 +6212,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/transform-rotate/-/transform-rotate-6.5.0.tgz", "integrity": "sha512-A2Ip1v4246ZmpssxpcL0hhiVBEf4L8lGnSPWTgSv5bWBEoya2fa/0SnFX9xJgP40rMP+ZzRaCN37vLHbv1Guag==", + "license": "MIT", "dependencies": { "@turf/centroid": "^6.5.0", "@turf/clone": "^6.5.0", @@ -5356,6 +6231,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/transform-scale/-/transform-scale-6.5.0.tgz", "integrity": "sha512-VsATGXC9rYM8qTjbQJ/P7BswKWXHdnSJ35JlV4OsZyHBMxJQHftvmZJsFbOqVtQnIQIzf2OAly6rfzVV9QLr7g==", + "license": "MIT", "dependencies": { "@turf/bbox": "^6.5.0", "@turf/center": "^6.5.0", @@ -5376,6 +6252,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/transform-translate/-/transform-translate-6.5.0.tgz", "integrity": "sha512-NABLw5VdtJt/9vSstChp93pc6oel4qXEos56RBMsPlYB8hzNTEKYtC146XJvyF4twJeeYS8RVe1u7KhoFwEM5w==", + "license": "MIT", "dependencies": { "@turf/clone": "^6.5.0", "@turf/helpers": "^6.5.0", @@ -5391,6 +6268,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/union/-/union-6.5.0.tgz", "integrity": "sha512-igYWCwP/f0RFHIlC2c0SKDuM/ObBaqSljI3IdV/x71805QbIvY/BYGcJdyNcgEA6cylIGl/0VSlIbpJHZ9ldhw==", + "license": "MIT", "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0", @@ -5447,16 +6325,14 @@ }, "node_modules/@types/brotli": { "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@types/brotli/-/brotli-1.3.4.tgz", - "integrity": "sha512-cKYjgaS2DMdCKF7R0F5cgx1nfBYObN2ihIuPGQ4/dlIY6RpV7OWNwe9L8V4tTVKL2eZqOkNM9FM/rgTvLf4oXw==", + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/crypto-js": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", - "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==" + "license": "MIT" }, "node_modules/@types/css-font-loading-module": { "version": "0.0.7", @@ -5494,12 +6370,14 @@ "node_modules/@types/estree": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "license": "MIT" }, "node_modules/@types/geojson": { "version": "7946.0.16", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", - "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==" + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" }, "node_modules/@types/geojson-vt": { "version": "3.2.5", @@ -5566,8 +6444,7 @@ }, "node_modules/@types/pako": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/pako/-/pako-1.0.7.tgz", - "integrity": "sha512-YBtzT2ztNF6R/9+UXj2wTGFnC9NklAnASt3sC0h2m1bbH7G6FyBIkt4AN8ThZpNfxUo1b2iMVO0UawiJymEt8A==" + "license": "MIT" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -5677,6 +6554,7 @@ "resolved": "https://registry.npmjs.org/@types/workerpool/-/workerpool-6.4.7.tgz", "integrity": "sha512-DI2U4obcMzFViyNjLw0xXspim++qkAJ4BWRdYPVMMFtOpTvMr6PAk3UTZEoSqnZnvgUkJ3ck97Ybk+iIfuJHMg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -5915,6 +6793,7 @@ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", @@ -6073,8 +6952,7 @@ }, "node_modules/@vivaxy/png": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vivaxy/png/-/png-1.3.0.tgz", - "integrity": "sha512-DkJnWnd451Y6VuuNXVnUD8V7Mg3ZeQ7F77YwH2sB8tyi4KrUEfaDvMyqlCSpSLlTmtsXlT8BOaegjMRg/VTRdQ==", + "license": "MIT", "dependencies": { "pako": "^1.0.10" } @@ -6229,6 +7107,7 @@ "version": "1.5.3", "resolved": "https://registry.npmjs.org/@webviz/group-tree-plot/-/group-tree-plot-1.5.3.tgz", "integrity": "sha512-Wgbg73AKFt/xd+VMYvHu2/4qoQtfK52lHCDoT6aNKH3oMhGjtzXIO5VdxqxNaqT5NkqUdqZOcC4vr2EFRaUsTg==", + "license": "MPL-2.0", "dependencies": { "d3": "^7.8.2", "lodash": "^4.17.21", @@ -6240,9 +7119,10 @@ } }, "node_modules/@webviz/subsurface-viewer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webviz/subsurface-viewer/-/subsurface-viewer-1.11.1.tgz", - "integrity": "sha512-xLexNjh0HbYiL5o0vqr2ksqOlCiumpxnXPMTtu7HRuXYqjEkwoMLfvM6wwLD0nQ8+LnbRPU0kXoohIt0vSXmPw==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@webviz/subsurface-viewer/-/subsurface-viewer-1.11.2.tgz", + "integrity": "sha512-urPdKPdgPPHx7WYEZ+20iiyWWvmJfbiUG+Wy92BNTnFbLjR3W+sA0AyzvKj7RKAdL1PGjsK+lbmT3f+/fGbZjA==", + "license": "MPL-2.0", "dependencies": { "@deck.gl-community/editable-layers": "^9.1.0-beta.4", "@deck.gl/aggregation-layers": "^9.1.11", @@ -6289,10 +7169,171 @@ } } }, + "node_modules/@webviz/subsurface-viewer/node_modules/@deck.gl-community/editable-layers": { + "version": "9.1.0-beta.5", + "resolved": "https://registry.npmjs.org/@deck.gl-community/editable-layers/-/editable-layers-9.1.0-beta.5.tgz", + "integrity": "sha512-kMlGepC81fRPE4Vuu9bsF4dv0Jz+5ZIfYRmfedWNyC1S9zJAFBBkIwkfFPEMC8IkL4Tjd1CRfCVyOcy3CqBd+w==", + "license": "MIT", + "dependencies": { + "@turf/along": "^6.5.0", + "@turf/area": "^6.5.0", + "@turf/bbox": "^6.5.0", + "@turf/bbox-polygon": "^6.5.0", + "@turf/bearing": "^6.5.0", + "@turf/boolean-point-in-polygon": "^6.5.0", + "@turf/buffer": "^6.5.0", + "@turf/center": "^6.5.0", + "@turf/centroid": "^6.5.0", + "@turf/circle": "^6.5.0", + "@turf/clone": "^6.5.0", + "@turf/destination": "^6.5.0", + "@turf/difference": "^6.5.0", + "@turf/distance": "^6.5.0", + "@turf/ellipse": "^6.5.0", + "@turf/helpers": "^6.5.0", + "@turf/intersect": "^6.5.0", + "@turf/invariant": "^6.5.0", + "@turf/line-intersect": "^6.5.0", + "@turf/meta": "^6.5.0", + "@turf/midpoint": "^6.5.0", + "@turf/nearest-point-on-line": "^6.5.0", + "@turf/point-to-line-distance": "^6.5.0", + "@turf/polygon-to-line": "^6.5.0", + "@turf/rewind": "^6.5.0", + "@turf/transform-rotate": "^6.5.0", + "@turf/transform-scale": "^6.5.0", + "@turf/transform-translate": "^6.5.0", + "@turf/union": "^6.5.0", + "@types/geojson": "^7946.0.14", + "cubic-hermite-spline": "^1.0.1", + "eventemitter3": "^5.0.0", + "lodash.omit": "^4.1.1", + "lodash.throttle": "^4.1.1", + "uuid": "9.0.0", + "viewport-mercator-project": ">=6.2.3" + }, + "peerDependencies": { + "@deck.gl-community/layers": "^9.1.0-beta.2", + "@deck.gl/core": "^9.1.0", + "@deck.gl/extensions": "^9.1.0", + "@deck.gl/geo-layers": "^9.1.0", + "@deck.gl/layers": "^9.1.0", + "@deck.gl/mesh-layers": "^9.1.0", + "@luma.gl/constants": ">=9.1.0", + "@luma.gl/core": ">=9.1.0", + "@luma.gl/engine": ">=9.1.0", + "@math.gl/core": ">=4.0.1" + } + }, + "node_modules/@webviz/subsurface-viewer/node_modules/@deck.gl/aggregation-layers": { + "version": "9.1.11", + "resolved": "https://registry.npmjs.org/@deck.gl/aggregation-layers/-/aggregation-layers-9.1.11.tgz", + "integrity": "sha512-3sVHcNsLj8Lzevf/2uZUn4pv6Z5Bpz7CzwataQ0EWGjjzHJ/4uIi24EIYEJmTzLGueenFS+70s8hWujJaAJMoQ==", + "license": "MIT", + "dependencies": { + "@luma.gl/constants": "^9.1.5", + "@luma.gl/shadertools": "^9.1.5", + "@math.gl/core": "^4.1.0", + "@math.gl/web-mercator": "^4.1.0", + "d3-hexbin": "^0.2.1" + }, + "peerDependencies": { + "@deck.gl/core": "^9.1.0", + "@deck.gl/layers": "^9.1.0", + "@luma.gl/core": "^9.1.5", + "@luma.gl/engine": "^9.1.5" + } + }, + "node_modules/@webviz/subsurface-viewer/node_modules/@deck.gl/extensions": { + "version": "9.1.11", + "resolved": "https://registry.npmjs.org/@deck.gl/extensions/-/extensions-9.1.11.tgz", + "integrity": "sha512-wnWUhd7yyQTdHB0x2fE/FPeZUz5ZzKNDx2FwY+bQuQVaiNcc5YpGKQSV0ahIKUg6NF+shxZIJ/WBVi3wc6RFRQ==", + "license": "MIT", + "dependencies": { + "@luma.gl/constants": "^9.1.5", + "@luma.gl/shadertools": "^9.1.5", + "@math.gl/core": "^4.1.0" + }, + "peerDependencies": { + "@deck.gl/core": "^9.1.0", + "@luma.gl/core": "^9.1.5", + "@luma.gl/engine": "^9.1.5" + } + }, + "node_modules/@webviz/subsurface-viewer/node_modules/@deck.gl/geo-layers": { + "version": "9.1.11", + "resolved": "https://registry.npmjs.org/@deck.gl/geo-layers/-/geo-layers-9.1.11.tgz", + "integrity": "sha512-z2rYT9617JaIP+a1nKhbxu32a5BjiR5L0Tmd4x7UUOlipx6A5850SyPyIUx5ca7lpIlbukGRjg600pRKKk3XEA==", + "license": "MIT", + "dependencies": { + "@loaders.gl/3d-tiles": "^4.2.0", + "@loaders.gl/gis": "^4.2.0", + "@loaders.gl/loader-utils": "^4.2.0", + "@loaders.gl/mvt": "^4.2.0", + "@loaders.gl/schema": "^4.2.0", + "@loaders.gl/terrain": "^4.2.0", + "@loaders.gl/tiles": "^4.2.0", + "@loaders.gl/wms": "^4.2.0", + "@luma.gl/gltf": "^9.1.5", + "@luma.gl/shadertools": "^9.1.5", + "@math.gl/core": "^4.1.0", + "@math.gl/culling": "^4.1.0", + "@math.gl/web-mercator": "^4.1.0", + "@types/geojson": "^7946.0.8", + "h3-js": "^4.1.0", + "long": "^3.2.0" + }, + "peerDependencies": { + "@deck.gl/core": "^9.1.0", + "@deck.gl/extensions": "^9.1.0", + "@deck.gl/layers": "^9.1.0", + "@deck.gl/mesh-layers": "^9.1.0", + "@loaders.gl/core": "^4.2.0", + "@luma.gl/core": "^9.1.5", + "@luma.gl/engine": "^9.1.5" + } + }, + "node_modules/@webviz/subsurface-viewer/node_modules/@deck.gl/layers": { + "version": "9.1.11", + "resolved": "https://registry.npmjs.org/@deck.gl/layers/-/layers-9.1.11.tgz", + "integrity": "sha512-RDFF+YH4BP3HPUCtblnfTaWXBt1DBk0i7FbQ9jgcjdv4CB7MtEOHme5k2LJ0cBV4ROQtSHg/vjAvMayZewI4vg==", + "license": "MIT", + "dependencies": { + "@loaders.gl/images": "^4.2.0", + "@loaders.gl/schema": "^4.2.0", + "@luma.gl/shadertools": "^9.1.5", + "@mapbox/tiny-sdf": "^2.0.5", + "@math.gl/core": "^4.1.0", + "@math.gl/polygon": "^4.1.0", + "@math.gl/web-mercator": "^4.1.0", + "earcut": "^2.2.4" + }, + "peerDependencies": { + "@deck.gl/core": "^9.1.0", + "@loaders.gl/core": "^4.2.0", + "@luma.gl/core": "^9.1.5", + "@luma.gl/engine": "^9.1.5" + } + }, + "node_modules/@webviz/subsurface-viewer/node_modules/@deck.gl/mesh-layers": { + "version": "9.1.11", + "resolved": "https://registry.npmjs.org/@deck.gl/mesh-layers/-/mesh-layers-9.1.11.tgz", + "integrity": "sha512-9zdtRV8rVyJI4t8bPE3/jLEuiBfCp+53KneFrh/nmANVupZ5SP44TobSWJWl+nZ3Xs4LkJXaQPbI8gZR39lJBQ==", + "license": "MIT", + "dependencies": { + "@loaders.gl/gltf": "^4.2.0", + "@luma.gl/gltf": "^9.1.5", + "@luma.gl/shadertools": "^9.1.5" + }, + "peerDependencies": { + "@deck.gl/core": "^9.1.0", + "@luma.gl/core": "^9.1.5", + "@luma.gl/engine": "^9.1.5" + } + }, "node_modules/@webviz/subsurface-viewer/node_modules/@equinor/eds-core-react": { "version": "0.36.1", - "resolved": "https://registry.npmjs.org/@equinor/eds-core-react/-/eds-core-react-0.36.1.tgz", - "integrity": "sha512-cFpmsT4+EEFDhGE1DLNDT9Scr6SNBF4xnIfAgkMZcK6wmmZZT30lV2zdGgFC1JN9FfyvlisQukgpurynuBoJTw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.24.0", "@equinor/eds-icons": "^0.21.0", @@ -6307,33 +7348,98 @@ "pnpm": ">=4" }, "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8", - "styled-components": ">=4.2" + "react": ">=16.8", + "react-dom": ">=16.8", + "styled-components": ">=4.2" + } + }, + "node_modules/@webviz/subsurface-viewer/node_modules/@equinor/eds-utils": { + "version": "0.8.4", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.8", + "@equinor/eds-tokens": "0.9.2" + }, + "engines": { + "node": ">=10.0.0", + "pnpm": ">=4" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8", + "styled-components": ">=4.2" + } + }, + "node_modules/@webviz/subsurface-viewer/node_modules/@luma.gl/constants": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/constants/-/constants-9.1.9.tgz", + "integrity": "sha512-yc9fml04OeTTcwK+7gmDMxoLQ67j4ZiAFXjmYvPomYyBVzS0NZxTDuwcCBmnxjLOiroOZW8FRRrVc/yOiFug2w==", + "license": "MIT" + }, + "node_modules/@webviz/subsurface-viewer/node_modules/@luma.gl/core": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/core/-/core-9.1.9.tgz", + "integrity": "sha512-1i9N7+I/UbFjx3axSMlc3/NufA+C2iBv/7mw51gRE/ypQPgvFmY/QqXBVZRe+nthF+OhlUMhO19TBndzYFTWhA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@math.gl/types": "^4.1.0", + "@probe.gl/env": "^4.0.8", + "@probe.gl/log": "^4.0.8", + "@probe.gl/stats": "^4.0.8", + "@types/offscreencanvas": "^2019.6.4" + } + }, + "node_modules/@webviz/subsurface-viewer/node_modules/@luma.gl/engine": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/engine/-/engine-9.1.9.tgz", + "integrity": "sha512-n1GLK1sUMFkWxdb+aZYn6ZBFltFEMi7X+6ZPxn2pBsNT6oeF4AyvH5AyqhOpvHvUnCLDt3Zsf1UIfx3MI//YSw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@math.gl/core": "^4.1.0", + "@math.gl/types": "^4.1.0", + "@probe.gl/log": "^4.0.8", + "@probe.gl/stats": "^4.0.8" + }, + "peerDependencies": { + "@luma.gl/core": "^9.1.0", + "@luma.gl/shadertools": "^9.1.0" } }, - "node_modules/@webviz/subsurface-viewer/node_modules/@equinor/eds-utils": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@equinor/eds-utils/-/eds-utils-0.8.4.tgz", - "integrity": "sha512-njvqXd3Hzfy5vkEqnx+uEBAu00vnG/5R+gDgWCReVDjjUoHdQNcrqfjBLsGF2UungtO0LbYV8YuBP+9l4V7ywQ==", + "node_modules/@webviz/subsurface-viewer/node_modules/@luma.gl/gltf": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/gltf/-/gltf-9.1.9.tgz", + "integrity": "sha512-KgVBIFCtRO1oadgMDycMJA5s+q519l/fQBGAZpUcLfWsaEDQfdHW2NLdrK/00VDv46Ng8tN/O6uyH6E40uLcLw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.8", - "@equinor/eds-tokens": "0.9.2" + "@loaders.gl/core": "^4.2.0", + "@loaders.gl/textures": "^4.2.0", + "@math.gl/core": "^4.1.0" }, - "engines": { - "node": ">=10.0.0", - "pnpm": ">=4" + "peerDependencies": { + "@luma.gl/core": "^9.1.0", + "@luma.gl/engine": "^9.1.0", + "@luma.gl/shadertools": "^9.1.0" + } + }, + "node_modules/@webviz/subsurface-viewer/node_modules/@luma.gl/shadertools": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@luma.gl/shadertools/-/shadertools-9.1.9.tgz", + "integrity": "sha512-Uqp2xfgIEunRMLXTeCJ4uEMlWcUGcYMZGJ8GAOrAeDzn4bMKVRKmZDC71vkuTctnaodM3UdrI9W6s1sJlrXsxw==", + "license": "MIT", + "dependencies": { + "@math.gl/core": "^4.1.0", + "@math.gl/types": "^4.1.0", + "wgsl_reflect": "^1.2.0" }, "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8", - "styled-components": ">=4.2" + "@luma.gl/core": "^9.1.0" } }, "node_modules/@webviz/subsurface-viewer/node_modules/@tanstack/react-virtual": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.1.3.tgz", - "integrity": "sha512-YCzcbF/Ws/uZ0q3Z6fagH+JVhx4JLvbSflgldMgLsuvB8aXjZLLb3HvrEVxY480F9wFlBiXlvQxOyXb5ENPrNA==", + "license": "MIT", "dependencies": { "@tanstack/virtual-core": "3.1.3" }, @@ -6348,8 +7454,7 @@ }, "node_modules/@webviz/subsurface-viewer/node_modules/@tanstack/virtual-core": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.1.3.tgz", - "integrity": "sha512-Y5B4EYyv1j9V8LzeAoOVeTg0LI7Fo5InYKgAjkY1Pu9GjtUwX/EKxNcU7ng3sKr99WEf+bPTcktAeybyMOYo+g==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -6357,16 +7462,14 @@ }, "node_modules/@webviz/subsurface-viewer/node_modules/d3-format": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/@webviz/subsurface-viewer/node_modules/downshift": { "version": "8.3.3", - "resolved": "https://registry.npmjs.org/downshift/-/downshift-8.3.3.tgz", - "integrity": "sha512-f9znQFYF/3AWBkFiEc4H05Vdh41XFgJ80IatLBKIFoA3p86mAXc/iM9/XJ24loF9djtABD5NBEYL7b1b7xh2pw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.22.15", "compute-scroll-into-view": "^3.0.3", @@ -6378,10 +7481,17 @@ "react": ">=16.12.0" } }, + "node_modules/@webviz/subsurface-viewer/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/@webviz/well-completions-plot": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@webviz/well-completions-plot/-/well-completions-plot-1.7.3.tgz", "integrity": "sha512-3cI2Wkjefq2xJYrLZfOwj6PR1Lqz140y9m3H4YXWa+Twi2nXGGlEHsqpemJr0wD8aCWchQPw9+HjJlYkvqckAQ==", + "license": "MPL-2.0", "dependencies": { "react-resize-detector": "^11.0.1", "react-tooltip": "^5.28.0" @@ -6402,6 +7512,7 @@ "version": "2.4.4", "resolved": "https://registry.npmjs.org/@webviz/well-log-viewer/-/well-log-viewer-2.4.4.tgz", "integrity": "sha512-quduVDS8ZBMPgdW+5YbbUGycitMY44ZZ7+rr9YfrEvWSWi/wRTgFg1eh0AmzKNiP6NmPXZxmIZ7oMauyGbunhw==", + "license": "MPL-2.0", "dependencies": { "@emerson-eps/color-tables": "^0.4.92", "@equinor/videx-wellog": "^0.11.3", @@ -6759,6 +7870,7 @@ "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "license": "MIT", "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", @@ -6768,12 +7880,14 @@ "node_modules/asn1.js/node_modules/bn.js": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==" + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" }, "node_modules/assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "is-nan": "^1.3.2", @@ -6858,8 +7972,6 @@ }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "funding": [ { "type": "github", @@ -6873,7 +7985,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/binary-extensions": { "version": "2.2.0", @@ -6910,7 +8023,8 @@ "node_modules/bn.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", - "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==" + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "license": "MIT" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -6934,12 +8048,12 @@ "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "license": "MIT" }, "node_modules/brotli": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", "optional": true, "dependencies": { "base64-js": "^1.1.2" @@ -6949,6 +8063,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "license": "MIT", "dependencies": { "resolve": "^1.17.0" } @@ -6957,6 +8072,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "license": "MIT", "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -6970,6 +8086,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "license": "MIT", "dependencies": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", @@ -6980,6 +8097,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "license": "MIT", "dependencies": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", @@ -6991,6 +8109,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz", "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", + "license": "MIT", "dependencies": { "bn.js": "^5.2.1", "randombytes": "^2.1.0", @@ -7004,6 +8123,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "license": "ISC", "dependencies": { "bn.js": "^5.2.1", "browserify-rsa": "^4.1.0", @@ -7024,6 +8144,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "license": "MIT", "dependencies": { "pako": "~1.0.5" } @@ -7062,8 +8183,7 @@ }, "node_modules/buf-compare": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buf-compare/-/buf-compare-1.0.1.tgz", - "integrity": "sha512-Bvx4xH00qweepGc43xFvMs5BKASXTbHaHm6+kDYIK9p/4iFwjATQkmPKHQSgJZzKbAymhztRbXUf1Nqhzl73/Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -7086,6 +8206,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -7099,12 +8220,14 @@ "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "license": "MIT" }, "node_modules/builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==" + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "license": "MIT" }, "node_modules/c12": { "version": "2.0.1", @@ -7285,8 +8408,7 @@ }, "node_modules/charenc": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "license": "BSD-3-Clause", "engines": { "node": "*" } @@ -7356,6 +8478,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz", "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", + "license": "MIT", "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1" @@ -7481,9 +8604,8 @@ "license": "MIT" }, "node_modules/complex.js": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.2.tgz", - "integrity": "sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==", + "version": "2.3.0", + "license": "MIT", "engines": { "node": "*" }, @@ -7538,7 +8660,8 @@ "node_modules/constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==" + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "license": "MIT" }, "node_modules/convert-source-map": { "version": "1.9.0", @@ -7555,8 +8678,7 @@ }, "node_modules/core-assert": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/core-assert/-/core-assert-0.2.1.tgz", - "integrity": "sha512-IG97qShIP+nrJCXMCgkNZgH7jZQ4n8RpPyPeXX++T6avR/KhLhgLiHKoEn5Rc1KjfycSfA9DMa6m+4C4eguHhw==", + "license": "MIT", "dependencies": { "buf-compare": "^1.0.0", "is-error": "^2.2.0" @@ -7603,6 +8725,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "license": "MIT", "dependencies": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" @@ -7611,12 +8734,14 @@ "node_modules/create-ecdh/node_modules/bn.js": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==" + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "license": "MIT", "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -7629,6 +8754,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "license": "MIT", "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -7641,7 +8767,8 @@ "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -7659,8 +8786,7 @@ }, "node_modules/crypt": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "license": "BSD-3-Clause", "engines": { "node": "*" } @@ -7669,6 +8795,7 @@ "version": "3.12.1", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz", "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", + "license": "MIT", "dependencies": { "browserify-cipher": "^1.0.1", "browserify-sign": "^4.2.3", @@ -7833,7 +8960,8 @@ "node_modules/cubic-hermite-spline": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cubic-hermite-spline/-/cubic-hermite-spline-1.0.1.tgz", - "integrity": "sha512-OlfZfJqnCi44aYNg3YMn0IqYcvlUGv3SzRqNbm19cnZNTaMiWjFeA5l6rF/WLnmh1VBZs/kYc2QwAkD1t2Zhdg==" + "integrity": "sha512-OlfZfJqnCi44aYNg3YMn0IqYcvlUGv3SzRqNbm19cnZNTaMiWjFeA5l6rF/WLnmh1VBZs/kYc2QwAkD1t2Zhdg==", + "license": "MIT" }, "node_modules/culori": { "version": "3.2.0", @@ -7850,6 +8978,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", "dependencies": { "uniq": "^1.0.0" } @@ -8054,6 +9183,7 @@ "version": "1.7.1", "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.7.1.tgz", "integrity": "sha512-O4AempWAr+P5qbk2bC2FuN/sDW4z+dN2wDf9QV3bxQt4M5HfOEeXLgJ/UKQW0+o1Dj8BE+L5kiDbdWUMjsmQpw==", + "license": "BSD-3-Clause", "dependencies": { "d3-array": "1" } @@ -8092,7 +9222,8 @@ "node_modules/d3-geo/node_modules/d3-array": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", - "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==", + "license": "BSD-3-Clause" }, "node_modules/d3-hexbin": { "version": "0.2.2", @@ -8388,8 +9519,7 @@ }, "node_modules/deep-strict-equal": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/deep-strict-equal/-/deep-strict-equal-0.2.0.tgz", - "integrity": "sha512-3daSWyvZ/zwJvuMGlzG1O+Ow0YSadGfb3jsh9xoCutv2tWyB9dA4YvR9L9/fSdDZa2dByYQe+TqapSGUrjnkoA==", + "license": "MIT", "dependencies": { "core-assert": "^0.2.0" }, @@ -8571,6 +9701,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "license": "MIT", "dependencies": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" @@ -8604,6 +9735,7 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "license": "MIT", "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", @@ -8613,7 +9745,8 @@ "node_modules/diffie-hellman/node_modules/bn.js": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==" + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" }, "node_modules/dom-helpers": { "version": "5.2.1", @@ -8628,6 +9761,7 @@ "version": "4.22.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz", "integrity": "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -8664,8 +9798,7 @@ }, "node_modules/draco3d": { "version": "1.5.7", - "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz", - "integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==" + "license": "Apache-2.0" }, "node_modules/draw-svg-path": { "version": "1.0.0", @@ -8744,6 +9877,7 @@ "version": "6.6.1", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "license": "MIT", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -8757,7 +9891,8 @@ "node_modules/elliptic/node_modules/bn.js": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==" + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -9075,8 +10210,7 @@ }, "node_modules/escape-latex": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + "license": "MIT" }, "node_modules/escape-string-regexp": { "version": "4.0.0", @@ -9687,6 +10821,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "license": "MIT", "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -9848,17 +10983,20 @@ "license": "MIT" }, "node_modules/fast-xml-parser": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "version": "4.5.0", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" } ], + "license": "MIT", "dependencies": { - "strnum": "^1.1.1" + "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" @@ -9874,8 +11012,7 @@ }, "node_modules/fflate": { "version": "0.7.4", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", - "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==" + "license": "MIT" }, "node_modules/figures": { "version": "5.0.0", @@ -10054,8 +11191,7 @@ }, "node_modules/fraction.js": { "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "license": "MIT", "engines": { "node": "*" }, @@ -10129,6 +11265,20 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -10195,6 +11345,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/geojson-rbush/-/geojson-rbush-3.2.0.tgz", "integrity": "sha512-oVltQTXolxvsz1sZnutlSuLDEcQAKYC/uXt9zDzJJ6bu0W+baTI8LZBaTup5afzibEH4N3jlq2p+a152wlBJ7w==", + "license": "MIT", "dependencies": { "@turf/bbox": "*", "@turf/helpers": "6.x", @@ -10206,7 +11357,8 @@ "node_modules/geojson-rbush/node_modules/@types/geojson": { "version": "7946.0.8", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", - "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==" + "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==", + "license": "MIT" }, "node_modules/geojson-vt": { "version": "3.2.1", @@ -10720,9 +11872,8 @@ "peer": true }, "node_modules/h3-js": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/h3-js/-/h3-js-4.2.1.tgz", - "integrity": "sha512-HYiUrq5qTRFqMuQu3jEHqxXLk1zsSJiby9Lja/k42wHjabZG7tN9rOuzT/PEFf+Wa7rsnHLMHRWIu0mgcJ0ewQ==", + "version": "4.1.0", + "license": "Apache-2.0", "engines": { "node": ">=4", "npm": ">=3", @@ -10821,6 +11972,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz", "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "license": "MIT", "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1" @@ -10833,6 +11985,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -10852,6 +12005,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "license": "MIT", "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -10886,7 +12040,8 @@ "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==" + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "license": "MIT" }, "node_modules/human-signals": { "version": "5.0.0", @@ -10946,8 +12101,7 @@ }, "node_modules/image-size": { "version": "0.7.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.7.5.tgz", - "integrity": "sha512-Hiyv+mXHfFEP7LzUL/llg9RwFxxY+o9N3JVLIeG5E7iFIFAalxvRU9UZthBdYDEVnzHMgjnKJPPpay5BWf1g9g==", + "license": "MIT", "bin": { "image-size": "bin/image-size.js" }, @@ -10957,8 +12111,7 @@ }, "node_modules/immediate": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + "license": "MIT" }, "node_modules/immutable": { "version": "4.3.1", @@ -11014,6 +12167,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/inorder-tree-layout/-/inorder-tree-layout-1.0.0.tgz", "integrity": "sha512-nRgl0K3Cd8LC5U3qNzhwwDFC9CIGy0O/RN52L9woY0nqS0UkasEP752l2zxTql3P93FgNuewQKjmsNYNYw4xmA==", + "license": "MIT", "dependencies": { "bit-twiddle": "~0.0.1" } @@ -11021,7 +12175,8 @@ "node_modules/inorder-tree-layout/node_modules/bit-twiddle": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-0.0.2.tgz", - "integrity": "sha512-76iFAOrkcuw5UPA30Pt32XaytMHXz/04JembgIwsQAp7ImHYSWNq1shBbrlWf6CUvh1+amQ81LI8hNhqQgsBEw==" + "integrity": "sha512-76iFAOrkcuw5UPA30Pt32XaytMHXz/04JembgIwsQAp7ImHYSWNq1shBbrlWf6CUvh1+amQ81LI8hNhqQgsBEw==", + "license": "MIT" }, "node_modules/internal-slot": { "version": "1.1.0", @@ -11056,6 +12211,7 @@ "version": "10.7.16", "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", + "license": "BSD-3-Clause", "dependencies": { "@formatjs/ecma402-abstract": "2.3.4", "@formatjs/fast-memoize": "2.2.7", @@ -11066,12 +12222,14 @@ "node_modules/iota-array": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", - "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -11173,8 +12331,7 @@ }, "node_modules/is-buffer": { "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "license": "MIT" }, "node_modules/is-bun-module": { "version": "1.3.0", @@ -11256,8 +12413,7 @@ }, "node_modules/is-error": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.2.tgz", - "integrity": "sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==" + "license": "MIT" }, "node_modules/is-extglob": { "version": "2.1.1", @@ -11382,6 +12538,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" @@ -11622,6 +12779,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz", "integrity": "sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==", + "license": "MIT", "engines": { "node": ">=10" } @@ -11744,8 +12902,7 @@ }, "node_modules/javascript-natural-sort": { "version": "0.7.1", - "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" + "license": "MIT" }, "node_modules/jest-worker": { "version": "27.5.1", @@ -11851,17 +13008,6 @@ "node": ">= 6.0.0" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -11916,8 +13062,7 @@ }, "node_modules/jszip": { "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", @@ -11959,9 +13104,8 @@ } }, "node_modules/ktx-parse": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.7.1.tgz", - "integrity": "sha512-FeA3g56ksdFNwjXJJsc1CCc7co+AJYDp6ipIp878zZ2bU8kWROatLYf39TQEd4/XRSUvBXovQ8gaVKWPXsCLEQ==" + "version": "0.0.4", + "license": "MIT" }, "node_modules/levn": { "version": "0.4.1", @@ -11977,8 +13121,7 @@ }, "node_modules/lie": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", "dependencies": { "immediate": "~3.0.5" } @@ -12092,7 +13235,8 @@ "node_modules/lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" }, "node_modules/lodash._basebind": { "version": "2.3.0", @@ -12236,7 +13380,8 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", - "deprecated": "This package is deprecated. Use destructuring assignment syntax instead." + "deprecated": "This package is deprecated. Use destructuring assignment syntax instead.", + "license": "MIT" }, "node_modules/lodash.support": { "version": "2.3.0", @@ -12248,12 +13393,12 @@ "node_modules/lodash.throttle": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" }, "node_modules/long": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", - "integrity": "sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg==", + "license": "Apache-2.0", "engines": { "node": ">=0.6" } @@ -12279,20 +13424,19 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, "node_modules/lz4js": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/lz4js/-/lz4js-0.2.0.tgz", - "integrity": "sha512-gY2Ia9Lm7Ep8qMiuGRhvUq0Q7qUereeldZPP1PMEJxPtEWHJLqw9pgX68oHajBH0nzJK4MaZEA/YNV3jT8u8Bg==", + "license": "ISC", "optional": true }, "node_modules/lzo-wasm": { "version": "0.0.4", - "resolved": "https://registry.npmjs.org/lzo-wasm/-/lzo-wasm-0.0.4.tgz", - "integrity": "sha512-VKlnoJRFrB8SdJhlVKvW5vI1gGwcZ+mvChEXcSX6r2xDNc/Q2FD9esfBmGCuPZdrJ1feO+YcVFd2PTk0c137Gw==" + "license": "BSD-2-Clause" }, "node_modules/magic-string": { "version": "0.30.17", @@ -12486,7 +13630,8 @@ "node_modules/material-colors": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", - "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==", + "license": "ISC" }, "node_modules/math-intrinsics": { "version": "1.1.0", @@ -12506,19 +13651,17 @@ }, "node_modules/math.gl": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/math.gl/-/math.gl-4.1.0.tgz", - "integrity": "sha512-FtvCJuuAlvn4358e2SkepTv2gnV7VTvu0y/hwkkjS/urDk+nY9x/4Tsn19LmaJl1wqKaqn+QFZhbnjAsuMOkWA==", + "license": "MIT", "dependencies": { "@math.gl/core": "4.1.0" } }, "node_modules/mathjs": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-13.2.3.tgz", - "integrity": "sha512-I67Op0JU7gGykFK64bJexkSAmX498x0oybxfVXn1rroEMZTmfxppORhnk8mEUnPrbTfabDKCqvm18vJKMk2UJQ==", + "version": "13.2.0", + "license": "Apache-2.0", "dependencies": { - "@babel/runtime": "^7.25.7", - "complex.js": "^2.2.5", + "@babel/runtime": "^7.25.6", + "complex.js": "^2.1.1", "decimal.js": "^10.4.3", "escape-latex": "^1.2.0", "fraction.js": "^4.3.7", @@ -12536,8 +13679,7 @@ }, "node_modules/md5": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "license": "BSD-3-Clause", "dependencies": { "charenc": "0.0.2", "crypt": "0.0.2", @@ -12548,6 +13690,7 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "license": "MIT", "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -12556,8 +13699,7 @@ }, "node_modules/merge-refs": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-1.3.0.tgz", - "integrity": "sha512-nqXPXbso+1dcKDpPCXvwZyJILz+vSLqGGOnDrYHQYE+B8n9JTCekVLC65AfCpR4ggVyA/45Y0iR9LDyS2iI+zA==", + "license": "MIT", "funding": { "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1" }, @@ -12598,6 +13740,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "license": "MIT", "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" @@ -12609,7 +13752,8 @@ "node_modules/miller-rabin/node_modules/bn.js": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==" + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" }, "node_modules/mime-db": { "version": "1.52.0", @@ -12642,12 +13786,14 @@ "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" }, "node_modules/minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" }, "node_modules/minimatch": { "version": "3.1.2", @@ -12708,7 +13854,8 @@ "node_modules/mjolnir.js": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mjolnir.js/-/mjolnir.js-3.0.0.tgz", - "integrity": "sha512-siX3YCG7N2HnmN1xMH3cK4JkUZJhbkhRFJL+G5N1vH0mh1t5088rJknIoqDFWDIU6NPGvRRgLnYW3ZHjSMEBLA==" + "integrity": "sha512-siX3YCG7N2HnmN1xMH3cK4JkUZJhbkhRFJL+G5N1vH0mh1t5088rJknIoqDFWDIU6NPGvRRgLnYW3ZHjSMEBLA==", + "license": "MIT" }, "node_modules/mkdirp": { "version": "1.0.4", @@ -12868,6 +14015,7 @@ "version": "1.0.19", "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", "dependencies": { "iota-array": "^1.0.0", "is-buffer": "^1.0.2" @@ -12877,6 +14025,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", "dependencies": { "cwise-compiler": "^1.0.0" } @@ -12885,6 +14034,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ndarray-pack/-/ndarray-pack-1.2.1.tgz", "integrity": "sha512-51cECUJMT0rUZNQa09EoKsnFeDL4x2dHRT0VR5U2H5ZgEcm95ZDWcMA5JShroXjHOejmAD/fg8+H+OvUnVXz2g==", + "license": "MIT", "dependencies": { "cwise-compiler": "^1.1.2", "ndarray": "^1.0.13" @@ -12894,6 +14044,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/ndarray-scratch/-/ndarray-scratch-1.2.0.tgz", "integrity": "sha512-a4pASwB1jQyJcKLYrwrladVfDZDUGc78qLJZbHyb1Q4rhte0URhzc6ALQpBcauwgov0sXLwZz3vYH5jKAhSMIg==", + "license": "MIT", "dependencies": { "ndarray": "^1.0.14", "ndarray-ops": "^1.2.1", @@ -12903,7 +14054,8 @@ "node_modules/ndarray-select": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ndarray-select/-/ndarray-select-1.0.1.tgz", - "integrity": "sha512-bUQH+V2TQnPA0tiB6pliaUSYUTld6Il3UILaH/7T3S7RKzuW9RdA08hayeRJr04Xu4yNTpccLL3YYz7rNd7ffw==" + "integrity": "sha512-bUQH+V2TQnPA0tiB6pliaUSYUTld6Il3UILaH/7T3S7RKzuW9RdA08hayeRJr04Xu4yNTpccLL3YYz7rNd7ffw==", + "license": "MIT" }, "node_modules/needle": { "version": "2.9.1", @@ -12955,6 +14107,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-stdlib-browser/-/node-stdlib-browser-1.3.1.tgz", "integrity": "sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==", + "license": "MIT", "dependencies": { "assert": "^2.0.0", "browser-resolve": "^2.0.0", @@ -12991,12 +14144,14 @@ "node_modules/node-stdlib-browser/node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "license": "MIT" }, "node_modules/node-stdlib-browser/node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -13101,6 +14256,7 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" @@ -13249,7 +14405,8 @@ "node_modules/os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==" + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "license": "MIT" }, "node_modules/own-keys": { "version": "1.0.1", @@ -13302,8 +14459,7 @@ }, "node_modules/pako": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + "license": "(MIT AND Zlib)" }, "node_modules/parent-module": { "version": "1.0.1", @@ -13325,6 +14481,7 @@ "version": "5.1.7", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "license": "ISC", "dependencies": { "asn1.js": "^4.10.1", "browserify-aes": "^1.2.0", @@ -13375,7 +14532,8 @@ "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", @@ -13453,6 +14611,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "license": "MIT", "dependencies": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -13538,6 +14697,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "license": "MIT", "dependencies": { "find-up": "^5.0.0" }, @@ -13592,6 +14752,21 @@ "node": ">=18" } }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/plotly.js": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-3.0.1.tgz", @@ -13824,6 +14999,7 @@ "version": "0.15.7", "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.7.tgz", "integrity": "sha512-nhfdr83ECBg6xtqOAJab1tbksbBAOMUltN60bU+llHVOL0e5Onm1WpAXXWXVB39L8AJFssoIhEVuy/S90MmotA==", + "license": "MIT", "dependencies": { "robust-predicates": "^3.0.2", "splaytree": "^3.1.0" @@ -13970,6 +15146,7 @@ "version": "10.26.6", "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.6.tgz", "integrity": "sha512-5SRRBinwpwkaD+OqlBDeITlRgvd8I8QlxHJw9AxSdMNV6O+LodN9nUyYGpSF7sadHjs6RzeFShMexC6DbtWr9g==", + "license": "MIT", "peer": true, "funding": { "type": "opencollective", @@ -14024,6 +15201,7 @@ "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", "engines": { "node": ">= 0.6.0" } @@ -14069,6 +15247,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "license": "MIT", "dependencies": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", @@ -14081,7 +15260,8 @@ "node_modules/public-encrypt/node_modules/bn.js": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==" + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" }, "node_modules/punycode": { "version": "2.3.0", @@ -14095,6 +15275,7 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" }, @@ -14156,6 +15337,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "license": "MIT", "dependencies": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" @@ -14165,6 +15347,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "license": "MIT", "dependencies": { "quickselect": "^2.0.0" } @@ -14193,6 +15376,7 @@ "version": "3.39.0", "resolved": "https://registry.npmjs.org/react-aria/-/react-aria-3.39.0.tgz", "integrity": "sha512-zXCjR01WnfW4uW0f294uWrvdfwEMHgDFSwMwMBwRafAvmsQea87X5VTAfDmQOAbPa+iQFcngIyH0Pn5CfXNrjw==", + "license": "Apache-2.0", "dependencies": { "@internationalized/string": "^3.2.6", "@react-aria/breadcrumbs": "^3.5.23", @@ -14246,6 +15430,7 @@ "version": "2.19.3", "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==", + "license": "MIT", "dependencies": { "@icons/material": "^0.2.4", "lodash": "^4.17.15", @@ -14290,6 +15475,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -14298,6 +15484,7 @@ "version": "9.1.1", "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-9.1.1.tgz", "integrity": "sha512-siLzop7i4xIvZIACE/PHTvRegA8QRCEt0TfmvJ/qCIFQJ4U+3NuYcF8tNDmDWxfIn+X1eNCyY2rauH4KRxge8w==", + "license": "MIT", "dependencies": { "lodash": "^4.17.21" }, @@ -14337,6 +15524,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "license": "MIT", "dependencies": { "lodash": "^4.0.1" } @@ -14575,6 +15763,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "license": "MIT", "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -14588,6 +15777,7 @@ "version": "4.40.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.2.tgz", "integrity": "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==", + "license": "MIT", "dependencies": { "@types/estree": "1.0.7" }, @@ -14778,8 +15968,7 @@ }, "node_modules/seedrandom": { "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + "license": "MIT" }, "node_modules/semver": { "version": "6.3.1", @@ -14886,13 +16075,13 @@ }, "node_modules/setimmediate": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + "license": "MIT" }, "node_modules/sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "license": "(MIT AND BSD-3-Clause)", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -15029,8 +16218,7 @@ }, "node_modules/snappyjs": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/snappyjs/-/snappyjs-0.6.1.tgz", - "integrity": "sha512-YIK6I2lsH072UE0aOFxxY1dPDCS43I5ktqHpeAsuLNYWkE5pGxRGWfDM4/vSUfNzXjC1Ivzt3qx31PCLmc9yqg==" + "license": "MIT" }, "node_modules/source-map": { "version": "0.5.7", @@ -15069,12 +16257,12 @@ "node_modules/splaytree": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.2.tgz", - "integrity": "sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A==" + "integrity": "sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A==", + "license": "MIT" }, "node_modules/sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "license": "BSD-3-Clause" }, "node_modules/stable-hash": { "version": "0.0.4", @@ -15108,6 +16296,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/static-kdtree/-/static-kdtree-1.0.2.tgz", "integrity": "sha512-0EnWbb9V5TKZp+SvS59wzXGDFzA02YTpOfE73R1Exj2MK7fNyJELtAeSMW/p23nc2HFV1HqHxTS19aflt6f83g==", + "license": "MIT", "dependencies": { "bit-twiddle": "^1.0.0", "inorder-tree-layout": "^1.0.0", @@ -15130,6 +16319,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "license": "MIT", "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" @@ -15139,6 +16329,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -15152,6 +16343,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "license": "MIT", "dependencies": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.4", @@ -15163,6 +16355,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -15433,15 +16626,8 @@ } }, "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ] + "version": "1.0.5", + "license": "MIT" }, "node_modules/strongly-connected-components": { "version": "1.0.1", @@ -15761,8 +16947,7 @@ }, "node_modules/texture-compressor": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/texture-compressor/-/texture-compressor-1.0.2.tgz", - "integrity": "sha512-dStVgoaQ11mA5htJ+RzZ51ZxIZqNOgWKAIvtjLrW1AliQQLCmrDqNzQZ8Jh91YealQ95DXt4MEduLzJmbs6lig==", + "license": "MIT", "dependencies": { "argparse": "^1.0.10", "image-size": "^0.7.4" @@ -15773,8 +16958,7 @@ }, "node_modules/texture-compressor/node_modules/argparse": { "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } @@ -15792,6 +16976,7 @@ "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "license": "MIT", "dependencies": { "setimmediate": "^1.0.4" }, @@ -15801,8 +16986,7 @@ }, "node_modules/tiny-emitter": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + "license": "MIT" }, "node_modules/tiny-invariant": { "version": "1.3.3", @@ -15832,6 +17016,7 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "license": "MIT", "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" @@ -15847,6 +17032,7 @@ "version": "6.4.4", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -15860,6 +17046,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -16022,12 +17209,14 @@ "node_modules/tty-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", - "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==" + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "license": "MIT" }, "node_modules/turf-jsts": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/turf-jsts/-/turf-jsts-1.2.3.tgz", - "integrity": "sha512-Ja03QIJlPuHt4IQ2FfGex4F4JAr8m3jpaHbFbQrgwr7s7L6U8ocrHiF3J1+wf9jzhGKxvDeaCAnGDot8OjGFyA==" + "integrity": "sha512-Ja03QIJlPuHt4IQ2FfGex4F4JAr8m3jpaHbFbQrgwr7s7L6U8ocrHiF3J1+wf9jzhGKxvDeaCAnGDot8OjGFyA==", + "license": "(EDL-1.0 OR EPL-1.0)" }, "node_modules/type": { "version": "2.7.3", @@ -16122,8 +17311,7 @@ }, "node_modules/typed-function": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.1.tgz", - "integrity": "sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==", + "license": "MIT", "engines": { "node": ">= 18" } @@ -16228,7 +17416,8 @@ "node_modules/uniq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" }, "node_modules/unquote": { "version": "1.1.1", @@ -16283,6 +17472,7 @@ "version": "0.11.4", "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "license": "MIT", "dependencies": { "punycode": "^1.4.1", "qs": "^6.12.3" @@ -16299,6 +17489,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -16307,6 +17498,7 @@ "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -16331,6 +17523,7 @@ "resolved": "https://registry.npmjs.org/viewport-mercator-project/-/viewport-mercator-project-7.0.4.tgz", "integrity": "sha512-0jzpL6pIMocCKWg1C3mqi/N4UPgZC3FzwghEm1H+XsUo8hNZAyJc3QR7YqC816ibOR8aWT5pCsV+gCu8/BMJgg==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", "dependencies": { "@math.gl/web-mercator": "^3.5.5" } @@ -16339,6 +17532,7 @@ "version": "3.6.3", "resolved": "https://registry.npmjs.org/@math.gl/web-mercator/-/web-mercator-3.6.3.tgz", "integrity": "sha512-UVrkSOs02YLehKaehrxhAejYMurehIHPfFQvPFZmdJHglHOU4V2cCUApTVEwOksvCp161ypEqVp+9H6mGhTTcw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.0", "gl-matrix": "^3.4.0" @@ -16348,6 +17542,7 @@ "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -16573,10 +17768,28 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/vite-plugin-glsl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vite-plugin-glsl/-/vite-plugin-glsl-1.3.1.tgz", + "integrity": "sha512-iClII8Idb9X0m6nS0YI2cWWXbBuT5EKKw5kXSAuRu4RJsNe4oypxKXE7jx0XMoyqij2s8WL0ZLfou801mpkREg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">= 16.15.1", + "npm": ">= 8.11.0" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + } + }, "node_modules/vite-plugin-node-polyfills": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.23.0.tgz", "integrity": "sha512-4n+Ys+2bKHQohPBKigFlndwWQ5fFKwaGY6muNDMTb0fSQLyBzS+jjUNRZG9sKF0S/Go4ApG6LFnUGopjkILg3w==", + "license": "MIT", "dependencies": { "@rollup/plugin-inject": "^5.0.5", "node-stdlib-browser": "^1.2.0" @@ -16592,6 +17805,7 @@ "version": "6.4.4", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -16605,6 +17819,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -16690,7 +17905,8 @@ "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "license": "MIT" }, "node_modules/vscode-uri": { "version": "3.1.0", @@ -16854,7 +18070,8 @@ "node_modules/wgsl_reflect": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/wgsl_reflect/-/wgsl_reflect-1.2.0.tgz", - "integrity": "sha512-bDYcmWfbg4WsrBmPv6lsyjqXx02r8dkNAzR77OCNqIcR8snO4aNSBTjir9zqgR7rLnw6PaisiZxtCitSCIUlnQ==" + "integrity": "sha512-bDYcmWfbg4WsrBmPv6lsyjqXx02r8dkNAzR77OCNqIcR8snO4aNSBTjir9zqgR7rLnw6PaisiZxtCitSCIUlnQ==", + "license": "MIT" }, "node_modules/which": { "version": "2.0.2", @@ -16989,7 +18206,8 @@ "node_modules/workerpool": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.2.0.tgz", - "integrity": "sha512-PKZqBOCo6CYkVOwAxWxQaSF2Fvb5Iv2fCeTP7buyWI2GiynWr46NcXSgK/idoV6e60dgCBfgYc+Un3HMvmqP8w==" + "integrity": "sha512-PKZqBOCo6CYkVOwAxWxQaSF2Fvb5Iv2fCeTP7buyWI2GiynWr46NcXSgK/idoV6e60dgCBfgYc+Un3HMvmqP8w==", + "license": "Apache-2.0" }, "node_modules/world-calendars": { "version": "1.0.3", @@ -17101,7 +18319,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yaml": { "version": "2.7.0", @@ -17129,8 +18348,7 @@ }, "node_modules/zstd-codec": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/zstd-codec/-/zstd-codec-0.1.5.tgz", - "integrity": "sha512-v3fyjpK8S/dpY/X5WxqTK3IoCnp/ZOLxn144GZVlNUjtwAchzrVo03h+oMATFhCIiJ5KTr4V3vDQQYz4RU684g==", + "license": "MIT", "optional": true } } diff --git a/frontend/package.json b/frontend/package.json index 4627e2604..35e52552a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -60,7 +60,7 @@ "simplify-js": "^1.2.4", "uuid": "^9.0.0", "vite-plugin-node-polyfills": "^0.23.0", - "wonka": "^6.3.4" + "wonka": "^6.3.4", "workerpool": "^9.2.0" }, "devDependencies": { diff --git a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx index 5a482d8a8..ebcf8803b 100644 --- a/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx +++ b/frontend/src/modules/2DViewer/view/components/LayersWrapper.tsx @@ -34,7 +34,10 @@ import { makeDrilledWellborePicksBoundingBox } from "@modules/_shared/DataProvid import { makeDrilledWellTrajectoriesBoundingBox } from "@modules/_shared/DataProviderFramework/visualization/deckgl/boundingBoxes/makeDrilledWellTrajectoriesBoundingBox"; import { makeDrilledWellborePicksLayer } from "@modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellborePicksLayer"; import { makeDrilledWellTrajectoriesLayer } from "@modules/_shared/DataProviderFramework/visualization/deckgl/makeDrilledWellTrajectoriesLayer"; -import type { VisualizationTarget } from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; +import type { + Annotation, + VisualizationTarget, +} from "@modules/_shared/DataProviderFramework/visualization/VisualizationAssembler"; import { VisualizationAssembler, VisualizationItemType,