Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Emit interpolated curve information #2205

Merged
merged 9 commits into from
Aug 28, 2024
78 changes: 75 additions & 3 deletions typescript/packages/well-log-viewer/src/SyncLogViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import type {
import type { WellLogSpacerOptions } from "./components/WellLogSpacer";
import { getWellPicks } from "./components/WellLogView";

import { getAvailableAxes } from "./utils/tracks";
import { getAvailableAxes, toggleId } from "./utils/tracks";

import { checkMinMax } from "./utils/minmax";

Expand All @@ -39,6 +39,8 @@ import { onTrackMouseEventDefault } from "./utils/edit-track";
import type { Info, InfoOptions } from "./components/InfoTypes";

import { isEqualRanges, isEqDomains } from "./utils/log-viewer";
import type { LogViewer } from "@equinor/videx-wellog";
import { fillInfos } from "./utils/fill-info";

export function isEqualArrays(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -161,6 +163,14 @@ export interface SyncLogViewerProps {
layout?: ViewerLayout<SyncLogViewer>;

// callbacks
onInfo?: (
iWellLog: number,
x: number,
logController: LogViewer,
iFrom: number,
iTo: number
) => void;
onInfoFilled?: (iWellLog: number, infos: Info[]) => void;
onContentRescale?: (iWellLog: number) => void;
onContentSelection?: (iWellLog: number) => void;
onTemplateChanged?: (iWellLog: number) => void;
Expand Down Expand Up @@ -278,8 +288,15 @@ class SyncLogViewer extends Component<SyncLogViewerProps, State> {
onContentRescaleBind: () => void;
onContentSelectionBind: () => void;
onTemplateChangedBind: () => void;
onInfoBind: (
x: number,
logController: LogViewer,
iFrom: number,
iTo: number
) => void;
}[];

collapsedTrackIds: (string | number)[][];
controllers: WellLogController[]; // for onDeletecontroller implementation

_isMounted: boolean;
Expand All @@ -292,6 +309,7 @@ class SyncLogViewer extends Component<SyncLogViewerProps, State> {

this.callbacks = [];
this.callbackManagers = [];
this.collapsedTrackIds = [];

this.controllers = [];

Expand Down Expand Up @@ -392,6 +410,9 @@ class SyncLogViewer extends Component<SyncLogViewerProps, State> {
}

fillViewCallbacks(iView: number): void {
const collapsedTrackIds: (string | number)[] = [];
this.collapsedTrackIds.push(collapsedTrackIds);

const callbackManager = new CallbackManager(
() => this.props.welllogs[iView]
);
Expand All @@ -404,7 +425,17 @@ class SyncLogViewer extends Component<SyncLogViewerProps, State> {
true
);

// const onInfoFilled = this.onInfoFilled.bind(this, iView);
const onInfoFilled = (infos: Info[]) => {
this.props.onInfoFilled?.(iView, infos);
};

if (this.props.onInfoFilled) {
callbackManager.registerCallback("onInfoFilled", onInfoFilled);
}

this.callbacks.push({
onInfoBind: this.onInfo.bind(this, iView),
onCreateControllerBind: this.onCreateController.bind(this, iView),
onTrackScrollBind: this.onTrackScroll.bind(this, iView),
onTrackSelectionBind: this.onTrackSelection.bind(this, iView),
Expand Down Expand Up @@ -461,9 +492,51 @@ class SyncLogViewer extends Component<SyncLogViewerProps, State> {
callbackManager.callCallbacks("onInfoGroupClick", info);
i++;
}

// Collapse this views info group
const collapsedTrackIds = this.collapsedTrackIds[iWellLog];
toggleId(collapsedTrackIds, info.trackId);
this.callbackManagers[iWellLog].updateInfo();

this._inInfoGroupClick--;
}

onInfo(
iWellLog: number,
x: number,
logController: LogViewer,
iFrom: number,
iTo: number
) {
this.callbackManagers[iWellLog].onInfo(x, logController, iFrom, iTo);
this.props.onInfo?.(iWellLog, x, logController, iFrom, iTo);

this.fillInfo(iWellLog, x, logController, iFrom, iTo);
}

fillInfo(
iWellLog: number,
x: number,
logController: LogViewer,
iFrom: number,
iTo: number
) {
// Skip computations if no-one is listening to the result
if (this.callbackManagers[iWellLog].onInfoFilledCallbacks.length < 1)
return;

const interpolatedData = fillInfos(
x,
logController,
iFrom,
iTo,
this.collapsedTrackIds[iWellLog],
this.props.readoutOptions
);

this.callbackManagers[iWellLog].onInfoFilled(interpolatedData);
}

// callback function from WellLogView
onCreateController(iWellLog: number, controller: WellLogController): void {
this.callbackManagers[iWellLog]?.onCreateController(controller);
Expand Down Expand Up @@ -845,7 +918,6 @@ class SyncLogViewer extends Component<SyncLogViewerProps, State> {

createView(index: number): ReactNode {
const callbacks = this.callbacks[index];
const callbackManager = this.callbackManagers[index];
const wellLog = this.props.welllogs[index];
const templates = this.props.templates;
const template = templates[index] ? templates[index] : templates[0];
Expand Down Expand Up @@ -877,7 +949,7 @@ class SyncLogViewer extends Component<SyncLogViewerProps, State> {
primaryAxis={this.state.primaryAxis}
options={options}
// callbacks
onInfo={callbackManager.onInfo}
onInfo={callbacks.onInfoBind}
onCreateController={callbacks.onCreateControllerBind}
onTrackMouseEvent={
this.props.onTrackMouseEvent || onTrackMouseEventDefault
Expand Down
98 changes: 98 additions & 0 deletions typescript/packages/well-log-viewer/src/WellLogViewer.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ import colorTables from "../../../../example-data/wellpick_colors.json";
import wellPicks from "../../../../example-data/wellpicks.json";

import { axisMnemos, axisTitles } from "./utils/axes";

import type { Info } from "./components/InfoTypes";
import type WellLogView from "./components/WellLogView";

const ComponentCode =
Expand Down Expand Up @@ -380,6 +382,102 @@ export const Horizontal: StoryObj<typeof StoryTemplate> = {
render: (args) => <StoryTemplate {...args} />,
};

export const OnInfoFilledEvent: StoryObj<typeof StoryTemplate> = {
args: {
id: "Well-Log-Viewer-OnInfoFilled",
horizontal: true,
welllog:
require("../../../../example-data/WL_RAW_AAC-BHPR-CAL-DEN-GR-MECH-NEU-NMR-REMP_MWD_3.json")[0], // eslint-disable-line
template: require("../../../../example-data/welllog_template_2.json"), // eslint-disable-line

// @ts-expect-error TS2322
colorTables: colorTables,
// @ts-expect-error TS2322

wellpick: wellpick,
axisTitles: axisTitles,
axisMnemos: axisMnemos,
viewTitle: true, // show default welllog view title (a wellname from the welllog)
layout: { right: undefined },
},
parameters: {
docs: {
description: {
story: 'You can get the computed information at the current selection by using the "onInfoFilled" event. The event is called both externally (via the callback property), or internally (using the callback manager). This example shows an external floating panel using the callback property',
},
},
},
render: (args) => <StoryTemplateWithCustomPanel {...args} />,
};

function StoryTemplateWithCustomPanel(props: WellLogViewerProps): JSX.Element {
const [infos, setInfos] = React.useState<Info[]>([]);
const [showPanel, setShowPanel] = React.useState<boolean>(false);

const handleInfoFilled = React.useCallback((newInfos: Info[]) => {
setInfos(newInfos);
}, []);

return (
<div
// Show/hide the panel when we move the mouse away from the story
onMouseEnter={() => setShowPanel(true)}
onMouseLeave={() => setShowPanel(false)}
>
<StoryTemplate {...props} onInfoFilled={handleInfoFilled} />
{showPanel && <CustomInfoPanel infos={infos} />}
</div>
);
}

function CustomInfoPanel(props: { infos: Info[] }) {
const [mousePosition, setMousePosition] = React.useState({
x: -1000,
y: -1000,
});
React.useEffect(() => {
const updateMousePos = (evt: MouseEvent) => {
setMousePosition({ x: evt.clientX, y: evt.clientY });
};

window.addEventListener("mousemove", updateMousePos);
return () => window.removeEventListener("mousemove", updateMousePos);
}, []);

return (
<div
style={{
position: "fixed",
left: mousePosition.x + 20,
top: mousePosition.y + 2,
padding: "0.25rem",
border: "solid gray 1px",
borderRadius: "0.25rem",
zIndex: 9999,
background: "white",
pointerEvents: "none",
}}
>
<div>
{props.infos?.map((i: Info) => {
if (i.type === "separator") return null;
else
return (
<div key={i.trackId}>
<span style={{ fontWeight: 700 }}>
{i.name}
</span>
<span> {i.value.toFixed(3)}</span>

<span> {i.units}</span>
</div>
);
})}
</div>
</div>
);
}

class MapAndWellLogViewer extends React.Component<Props, State> {
public static propTypes?: WeakValidationMap<Props> | undefined;
callbackManager: CallbackManager;
Expand Down
78 changes: 75 additions & 3 deletions typescript/packages/well-log-viewer/src/WellLogViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ import type {
} from "./components/WellLogView";
import type WellLogView from "./components/WellLogView";

import { getAvailableAxes } from "./utils/tracks";
import { getAvailableAxes, toggleId } from "./utils/tracks";

import { onTrackMouseEventDefault } from "./utils/edit-track";

import { CallbackManager } from "./components/CallbackManager";

import type { InfoOptions } from "./components/InfoTypes";
import type { Info, InfoOptions } from "./components/InfoTypes";
import type { LogViewer } from "@equinor/videx-wellog";
import { fillInfos } from "./utils/fill-info";

export interface WellLogViewerProps extends WellLogViewWithScrollerProps {
readoutOptions?: InfoOptions; // options for readout
Expand All @@ -37,6 +39,7 @@ export interface WellLogViewerProps extends WellLogViewWithScrollerProps {
onContentSelection?: () => void;
onTemplateChanged?: () => void;

onInfoFilled?: (computedInfo: Info[]) => void;
onTrackMouseEvent?: (wellLogView: WellLogView, ev: TrackMouseEvent) => void;

onCreateController?: (controller: WellLogController) => void;
Expand Down Expand Up @@ -74,6 +77,7 @@ export default class WellLogViewer extends Component<
public static propTypes: Record<string, unknown>;

callbackManager: CallbackManager;
collapsedTrackIds: (string | number)[];

constructor(props: WellLogViewerProps) {
super(props);
Expand All @@ -83,14 +87,58 @@ export default class WellLogViewer extends Component<
};

this.callbackManager = new CallbackManager(() => this.props.welllog);
this.collapsedTrackIds = [];

this.onCreateController = this.onCreateController.bind(this);

this.onInfo = this.onInfo.bind(this);
this.onContentRescale = this.onContentRescale.bind(this);
this.onContentSelection = this.onContentSelection.bind(this);
this.onTemplateChanged = this.onTemplateChanged.bind(this);
this.fillInfo = this.fillInfo.bind(this);
this.onInfoGroupClick = this.onInfoGroupClick.bind(this);

this.onChangePrimaryAxis = this.onChangePrimaryAxis.bind(this);

this.callbackManager.registerCallback(
"onInfoGroupClick",
this.onInfoGroupClick,
true
);

if (props.onInfoFilled) {
this.callbackManager.registerCallback(
"onInfoFilled",
props.onInfoFilled
);
}
}

onInfoGroupClick(info: Info): void {
const collapsedTrackIds = this.collapsedTrackIds;
/*
const controller = this.props.callbackManager.controller;
if (controller) { // info.trackId could be for another controller so map iTrack to trackid for the curent controller
const wellLogView = controller as WellLogView;
const logController = wellLogView.logController;
const tracks = logController?.tracks;
if (tracks) {
let iTrack = 0;
for (const track of tracks) {
if (isScaleTrack(track)) continue;
if (info.iTrack == iTrack) {
toggleId(collapsedTrackIds, track.id);
break;
}
iTrack++;
}
}
}
else*/ {
// old code
toggleId(collapsedTrackIds, info.trackId);
}
this.callbackManager.updateInfo(); // force to get onInfo call from WellLogView
}

// callback function from WellLogView
Expand Down Expand Up @@ -119,6 +167,30 @@ export default class WellLogViewer extends Component<
this.callbackManager.onChangePrimaryAxis(value);
}

onInfo(x: number, logController: LogViewer, iFrom: number, iTo: number) {
this.callbackManager.onInfo(x, logController, iFrom, iTo);
this.props.onInfo?.(x, logController, iFrom, iTo);

this.fillInfo(x, logController, iFrom, iTo);
}

fillInfo(x: number, logController: LogViewer, iFrom: number, iTo: number) {
if (this.callbackManager.onInfoFilledCallbacks.length < 1) return;

const infoOptions = this.props.readoutOptions;

const interpolatedData = fillInfos(
x,
logController,
iFrom,
iTo,
this.collapsedTrackIds,
infoOptions
);

this.callbackManager.onInfoFilled(interpolatedData);
}

componentDidMount(): void {
this.onContentRescale();
const controller = this.callbackManager?.controller;
Expand Down Expand Up @@ -204,7 +276,7 @@ export default class WellLogViewer extends Component<
primaryAxis={this.state.primaryAxis}
options={this.props.options}
// callbacks
onInfo={this.callbackManager.onInfo}
onInfo={this.onInfo}
onCreateController={this.onCreateController}
onTrackMouseEvent={
this.props.onTrackMouseEvent ||
Expand Down
Loading
Loading