Skip to content

Commit

Permalink
Started implementing color scales
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenthoms committed Sep 17, 2024
1 parent 7115e1b commit 33e6d5e
Show file tree
Hide file tree
Showing 17 changed files with 227 additions and 49 deletions.
44 changes: 44 additions & 0 deletions frontend/src/modules/2DViewer/layers/ColorScale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { defaultContinuousSequentialColorPalettes } from "@framework/utils/colorPalettes";
import { ColorScaleGradientType, ColorScaleType } from "@lib/utils/ColorScale";
import { ColorScale as ColorScaleImpl } from "@lib/utils/ColorScale";

import { LayerManagerTopic } from "./LayerManager";
import { ItemDelegate } from "./delegates/ItemDelegate";
import { Item } from "./interfaces";

export class ColorScale implements Item {
private _itemDelegate: ItemDelegate;
private _colorScale: ColorScaleImpl = new ColorScaleImpl({
colorPalette: defaultContinuousSequentialColorPalettes[0],
gradientType: ColorScaleGradientType.Sequential,
type: ColorScaleType.Continuous,
steps: 10,
});
private _areBoundariesUserDefined: boolean = false;

constructor(name: string) {
this._itemDelegate = new ItemDelegate(name);
}

getItemDelegate(): ItemDelegate {
return this._itemDelegate;
}

getColorScale(): ColorScaleImpl {
return this._colorScale;
}

setColorScale(colorScale: ColorScaleImpl): void {
this._colorScale = colorScale;
this.getItemDelegate().getLayerManager()?.publishTopic(LayerManagerTopic.SETTINGS_CHANGED);
}

getAreBoundariesUserDefined(): boolean {
return this._areBoundariesUserDefined;
}

setAreBoundariesUserDefined(areBoundariesUserDefined: boolean): void {
this._areBoundariesUserDefined = areBoundariesUserDefined;
this.getItemDelegate().getLayerManager()?.publishTopic(LayerManagerTopic.LAYER_DATA_REVISION);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from "react";

import { SortableListItem } from "@lib/components/SortableList";
import { ColorScale as ColorScaleImpl } from "@lib/utils/ColorScale";
import { ColorScaleSelector } from "@modules/_shared/components/ColorScaleSelector/colorScaleSelector";

import { RemoveButtonComponent } from "./RemoveButtonComponent";

import { ColorScale } from "../ColorScale";

export type ColorScaleComponentProps = {
colorScale: ColorScale;
};

export function ColorScaleComponent(props: ColorScaleComponentProps): React.ReactNode {
const workbenchSettings = props.colorScale.getItemDelegate().getLayerManager()?.getWorkbenchSettings();

function handleColorScaleChange(newColorScale: ColorScaleImpl, areBoundariesUserDefined: boolean): void {
props.colorScale.setColorScale(newColorScale);
props.colorScale.setAreBoundariesUserDefined(areBoundariesUserDefined);
}

function makeColorScaleSelector(): React.ReactNode {
if (!workbenchSettings) {
return "No layer manager set.";
}

return (
<ColorScaleSelector
workbenchSettings={workbenchSettings}
colorScale={props.colorScale.getColorScale()}
onChange={handleColorScaleChange}
areBoundariesUserDefined={props.colorScale.getAreBoundariesUserDefined()}
/>
);
}

return (
<SortableListItem
key={props.colorScale.getItemDelegate().getId()}
id={props.colorScale.getItemDelegate().getId()}
title={<span className="font-bold">Color scale</span>}
endAdornment={<RemoveButtonComponent item={props.colorScale} />}
>
<div className="p-2">{makeColorScaleSelector()}</div>
</SortableListItem>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@ import { CircularProgress } from "@lib/components/CircularProgress";
import { SortableListItem } from "@lib/components/SortableList";
import { Check, Error } from "@mui/icons-material";

import { EditNameComponent } from "./EditNameComponent";
import { RemoveButtonComponent } from "./RemoveButtonComponent";
import { SettingComponent } from "./SettingComponent";
import { EditNameComponent } from "./editNameComponent";
import { RemoveButtonComponent } from "./removeButtonComponent";
import { VisibilityToggleComponent } from "./visibilityToggleComponent";
import { VisibilityToggleComponent } from "./VisibilityToggleComponent";

import { usePublishSubscribeTopicValue } from "../PublishSubscribeHandler";
import { LayerDelegateTopic } from "../delegates/LayerDelegate";
import { Layer, LayerStatus, Setting } from "../interfaces";

export type LayerComponentProps = {
layer: Layer<any, any>;
onRemove: (id: string) => void;
};

export function LayerComponent(props: LayerComponentProps): React.ReactNode {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SortableListGroup } from "@lib/components/SortableList";
import { SettingsApplications } from "@mui/icons-material";

import { LayersActionGroup, LayersActions } from "./layersActions";
import { RemoveButtonComponent } from "./removeButtonComponent";
import { LayersActionGroup, LayersActions } from "./LayersActions";
import { RemoveButtonComponent } from "./RemoveButtonComponent";
import { makeComponent } from "./utils";

import { usePublishSubscribeTopicValue } from "../PublishSubscribeHandler";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { SharedSetting } from "../SharedSetting";

export type SharedSettingComponentProps = {
sharedSetting: SharedSetting;
onRemove: (id: string) => void;
};

export function SharedSettingComponent(props: SharedSettingComponentProps): React.ReactNode {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { SortableListGroup } from "@lib/components/SortableList";

import { EditNameComponent } from "./editNameComponent";
import { LayersActionGroup, LayersActions } from "./layersActions";
import { RemoveButtonComponent } from "./removeButtonComponent";
import { EditNameComponent } from "./EditNameComponent";
import { LayersActionGroup, LayersActions } from "./LayersActions";
import { RemoveButtonComponent } from "./RemoveButtonComponent";
import { VisibilityToggleComponent } from "./VisibilityToggleComponent";
import { makeComponent } from "./utils";
import { VisibilityToggleComponent } from "./visibilityToggleComponent";

import { usePublishSubscribeTopicValue } from "../PublishSubscribeHandler";
import { GroupBaseTopic } from "../delegates/GroupDelegate";
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/modules/2DViewer/layers/components/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { SortableListItemProps } from "@lib/components/SortableList";

import { ColorScaleComponent } from "./ColorScaleComponent";
import { LayerComponent } from "./LayerComponent";
import { LayersActionGroup } from "./LayersActions";
import { SettingsGroupComponent } from "./SettingsGroupComponent";
import { SharedSettingComponent } from "./SharedSettingComponent";
import { ViewComponent } from "./ViewComponent";
import { LayersActionGroup } from "./layersActions";

import { ColorScale } from "../ColorScale";
import { SettingsGroup } from "../SettingsGroup";
import { SharedSetting } from "../SharedSetting";
import { View } from "../View";
Expand All @@ -17,7 +19,7 @@ export function makeComponent(
onActionClick?: (identifier: string, group: Group) => void
): React.ReactElement<SortableListItemProps> {
if (instanceofLayer(item)) {
return <LayerComponent key={item.getItemDelegate().getId()} layer={item} onRemove={() => {}} />;
return <LayerComponent key={item.getItemDelegate().getId()} layer={item} />;
}
if (instanceofGroup(item)) {
if (item instanceof SettingsGroup) {
Expand All @@ -41,7 +43,10 @@ export function makeComponent(
}
}
if (item instanceof SharedSetting) {
return <SharedSettingComponent key={item.getItemDelegate().getId()} sharedSetting={item} onRemove={() => {}} />;
return <SharedSettingComponent key={item.getItemDelegate().getId()} sharedSetting={item} />;
}
if (item instanceof ColorScale) {
return <ColorScaleComponent key={item.getItemDelegate().getId()} colorScale={item} />;
}
throw new Error("Not implemented");
}
13 changes: 13 additions & 0 deletions frontend/src/modules/2DViewer/layers/delegates/LayerDelegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export class LayerDelegate<TSettings extends Settings, TData>
private _data: TData | null = null;
private _error: StatusMessage | string | null = null;
private _boundingBox: BoundingBox | null = null;
private _valueRange: [number, number] | null = null;

constructor(owner: Layer<TSettings, TData>, settingsContext: SettingsContext<TSettings>) {
this._owner = owner;
Expand Down Expand Up @@ -80,6 +81,14 @@ export class LayerDelegate<TSettings extends Settings, TData>
this._boundingBox = null;
}

private invalidateValueRange(): void {
this._valueRange = null;
}

getValueRange(): [number, number] | null {
return this._valueRange;
}

setLayerManager(layerManager: LayerManager | null): void {
this._layerManager = layerManager;
this._settingsContext.getDelegate().setLayerManager(layerManager);
Expand Down Expand Up @@ -220,12 +229,16 @@ export class LayerDelegate<TSettings extends Settings, TData>

this.setStatus(LayerStatus.LOADING);
this.invalidateBoundingBox();
this.invalidateValueRange();

try {
this._data = await this._owner.fechData(queryClient);
if (this._owner.makeBoundingBox) {
this._boundingBox = this._owner.makeBoundingBox();
}
if (this._owner.makeValueRange) {
this._valueRange = this._owner.makeValueRange();
}
if (this._queryKeys.length === null && isDevMode()) {
console.warn(
"Did you forget to use 'setQueryKeys' in your layer implementation of 'fetchData'? This will cause the queries to not be cancelled when settings change and might lead to undesired behaviour."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ export class RealizationGridLayer
};
}

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];
}

fechData(queryClient: QueryClient): Promise<{
gridSurfaceData: GridSurface_trans;
gridParameterData: GridMappedProperty_trans;
Expand Down
1 change: 1 addition & 0 deletions frontend/src/modules/2DViewer/layers/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface Layer<TSettings extends Settings, TData> extends Item {
doSettingsChangesRequireDataRefetch(prevSettings: TSettings, newSettings: TSettings): boolean;
fechData(queryClient: QueryClient): Promise<TData>;
makeBoundingBox?(): BoundingBox | null;
makeValueRange?(): [number, number] | null;
}

export function instanceofLayer(item: Item): item is Layer<Settings, any> {
Expand Down
15 changes: 14 additions & 1 deletion frontend/src/modules/2DViewer/settings/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import { useSetAtom } from "jotai";

import { layerManagerAtom } from "./atoms/baseAtoms";

import { ColorScale } from "../layers/ColorScale";
import { LayerManager } from "../layers/LayerManager";
import { usePublishSubscribeTopicValue } from "../layers/PublishSubscribeHandler";
import { SettingsGroup } from "../layers/SettingsGroup";
import { SharedSetting } from "../layers/SharedSetting";
import { View } from "../layers/View";
import { LayersActionGroup, LayersActions } from "../layers/components/layersActions";
import { LayersActionGroup, LayersActions } from "../layers/components/LayersActions";
import { makeComponent } from "../layers/components/utils";
import { GroupBaseTopic } from "../layers/delegates/GroupDelegate";
import { DrilledWellTrajectoriesLayer } from "../layers/implementations/layers/DrilledWellTrajectoriesLayer/DrilledWellTrajectoriesLayer";
Expand Down Expand Up @@ -65,6 +66,9 @@ export function Settings(props: ModuleSettingsProps<any>): React.ReactNode {
case "settings-group":
groupDelegate.insertChild(new SettingsGroup("Settings group"), numSharedSettings);
return;
case "color-scale":
groupDelegate.insertChild(new ColorScale("Color scale"), numSharedSettings);
return;
case "observed_surface":
groupDelegate.insertChild(new ObservedSurfaceLayer(), numSharedSettings);
return;
Expand Down Expand Up @@ -276,6 +280,15 @@ const LAYER_ACTIONS: LayersActionGroup[] = [
},
],
},
{
label: "Color scales",
children: [
{
identifier: "color-scale",
label: "Color scale",
},
],
},
];

const VIEW_ACTIONS: LayersActionGroup[] = LAYER_ACTIONS.filter((group) => group.label !== "View");
47 changes: 24 additions & 23 deletions frontend/src/modules/2DViewer/view/layerFactory.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import { PolygonData_api, SurfaceDef_api, WellboreTrajectory_api } from "@api";
import { Layer } from "@deck.gl/core/typed";
import { GeoJsonLayer } from "@deck.gl/layers/typed";
import { ColorScale } from "@lib/utils/ColorScale";
import { Vec2, rotatePoint2Around } from "@lib/utils/vec2";
import { GridMappedProperty_trans, GridSurface_trans } from "@modules/3DViewer/view/queries/queryDataTransforms";
import { SurfaceDataFloat_trans } from "@modules/_shared/Surface/queryDataTransforms";
import {
ColormapLayer,
Grid3DLayer,
Hillshading2DLayer,
MapLayer,
WellsLayer,
} from "@webviz/subsurface-viewer/dist/layers";
import { ColorScaleWithName } from "@modules/_shared/utils/ColorScaleWithName";
import { ColormapLayer, Grid3DLayer, MapLayer, WellsLayer } from "@webviz/subsurface-viewer/dist/layers";

import { Rgb, parse } from "culori";
import { Feature } from "geojson";
import { SurfaceDataPng } from "src/api/models/SurfaceDataPng";

Expand All @@ -24,7 +19,7 @@ import { RealizationSurfaceLayer } from "../layers/implementations/layers/Realiz
import { StatisticalSurfaceLayer } from "../layers/implementations/layers/StatisticalSurfaceLayer/StatisticalSurfaceLayer";
import { Layer as LayerInterface } from "../layers/interfaces";

export function makeLayer(layer: LayerInterface<any, any>): Layer | null {
export function makeLayer(layer: LayerInterface<any, any>, colorScale?: ColorScaleWithName): Layer | null {
const data = layer.getLayerDelegate().getData();

if (!data) {
Expand All @@ -46,7 +41,13 @@ export function makeLayer(layer: LayerInterface<any, any>): Layer | null {
return makeWellsLayer(data, layer.getItemDelegate().getId(), null);
}
if (layer instanceof RealizationGridLayer) {
return makeGrid3DLayer(layer.getItemDelegate().getId(), data.gridSurfaceData, data.gridParameterData, false);
return makeGrid3DLayer(
layer.getItemDelegate().getId(),
data.gridSurfaceData,
data.gridParameterData,
false,
colorScale
);
}
return null;
}
Expand Down Expand Up @@ -252,13 +253,13 @@ export function makeGrid3DLayer(
id: string,
gridSurfaceData: GridSurface_trans,
gridParameterData: GridMappedProperty_trans,
showGridLines: boolean
showGridLines: boolean,
colorScale?: ColorScaleWithName
// colorScale: ColorScale
): 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;
console.log(gridParameterData);
const grid3dLayer = new Grid3DLayer({
id: id,
pointsData: pointsNumberArray,
Expand All @@ -271,17 +272,17 @@ export function makeGrid3DLayer(
colorMapName: "Physics",
colorMapClampColor: true,
colorMapRange: [gridParameterData.min_grid_prop_value, gridParameterData.max_grid_prop_value],
/*
colorMapFunction: (value: number) => {
const interpolatedColor = colorScale.getColorPalette().getInterpolatedColor(value);
// const nonNormalizedValue = value * (colorScale.getMax() - colorScale.getMin()) + colorScale.getMin();
const color = parse(interpolatedColor) as Rgb; // colorScale.getColorForValue(nonNormalizedValue)) as Rgb;
if (color === undefined) {
return [0, 0, 0];
}
return [color.r * 255, color.g * 255, color.b * 255];
},
*/
colorMapFunction: colorScale
? (value: number) => {
const interpolatedColor = colorScale.getColorPalette().getInterpolatedColor(value);
// const nonNormalizedValue = value * (colorScale.getMax() - colorScale.getMin()) + colorScale.getMin();
const color = parse(interpolatedColor) as Rgb; // colorScale.getColorForValue(nonNormalizedValue)) as Rgb;
if (color === undefined) {
return [0, 0, 0];
}
return [color.r * 255, color.g * 255, color.b * 255];
}
: undefined,
});
return grid3dLayer as unknown as WorkingGrid3dLayer;
}
Loading

0 comments on commit 33e6d5e

Please sign in to comment.