Last updated

Real-Time Deviated Track

The WellLog widget allows simulation of real-time flow monitoring. See the WellLog Widget tutorial.
While creating the trajectory and curves, auto-update is set to 'true' so the data sources are constantly updated with new samples. The tutorial also shows how a user can implement a cache to hold the data. If the collected data becomes too big, the cache can release the samples that were received first. The tutorial also shows how to handle limits and scrolling of the content based on realtime data. See the Real-time Server tutorial.

For information on big data, see the Big Data tutorial.

# Depth-based Real-time Deviated Track

Create a widget with deviated track and insert the widget in the plot and provide real-time data.

import { LogData } from "@int/geotoolkit/welllog/data/LogData.ts";
import { LogCurve } from "@int/geotoolkit/welllog/LogCurve.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Events as WellLogWidgetEvents } from "@int/geotoolkit/welllog/widgets/Events.ts";
import { HeaderType } from "@int/geotoolkit/welllog/header/LogAxisVisualHeader.ts";
import { TrackType } from "@int/geotoolkit/welllog/TrackType.ts";
import { ScrollToLocation } from "@int/geotoolkit/welllog/TrackContainer.ts";
import { defaults } from "/src/helpers/defaults.js";
import { Grid } from "@int/geotoolkit/axis/Grid.ts";
import { LogAxis } from "@int/geotoolkit/welllog/LogAxis.ts";
import { NumericLinearTickGenerator } from "@int/geotoolkit/axis/NumericLinearTickGenerator.ts";
import { LogTrack } from "@int/geotoolkit/welllog/LogTrack.ts";
import { RgbaColor } from "@int/geotoolkit/util/RgbaColor.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Trajectory2d } from "@int/geotoolkit/deviation/Trajectory2d.ts";
import { trajectoryData } from "/src/code/WellLog/DataAndTemplates/RealTimeDeviatedTrack/trajectory.ts";
import { MathUtil } from "@int/geotoolkit/util/MathUtil.ts";
import { DeviatedTrack } from "/src/code/WellLog/DataAndTemplates/RealTimeDeviatedTrack/DeviatedTrack.ts";
import { PiecewiseTransformer } from "@int/geotoolkit/deviation/PiecewiseTransformer.ts";
import { StackedTrack } from "@int/geotoolkit/welllog/StackedTrack.ts";
import curveData from "/src/helpers/curveData.json?import";
import { createWellLogWidget } from "/src/code/WellLog/utils/common.ts";
const CURVE1_NAME = "GR";
const CURVE2_NAME = "RHOB";
const TIME_REFRESH = 200;
const TOP_MARGIN = 50;
const MAX_CACHED_SIZE = 400;
const START_DEPTH = trajectoryData[1];
const DATA_TRACK_WIDTH = 70;
const DEPTH_TRACK_WIDTH = 50;
const DEV_TRACK_WIDTH = 650;
const STEP_MAJOR = 100;
const LEFT_DEVIATION_VALUE = -50;
const RIGHT_DEVIATION_VALUE = 700;
function createTrack(trackWidth) {
  return new LogTrack().setLayoutStyle({ "width": trackWidth }, true).enableClipping(true).setLineStyle({
    "color": "#bdbdbd",
    "pixelsnapmode": true
  });
}
function createDepthTrack(trackWidth, showLeftBorder, showRightBorder, name) {
  const tgDepth = new NumericLinearTickGenerator().setTickStep("major", STEP_MAJOR);
  return createTrack(trackWidth).setBorders({ "left": showLeftBorder, "right": showRightBorder }).addChild(new LogAxis(tgDepth).setBaseLineStyle(null).setName(name));
}
function createLinearTrack(trackWidth) {
  const tgValue = new NumericLinearTickGenerator().setTickStep("major", 0.5).setTickStep("minor", 0.25);
  const tgDepth = new NumericLinearTickGenerator().setTickStep("major", STEP_MAJOR);
  return createTrack(trackWidth).addChild(new Grid(tgDepth, tgValue));
}
function createCurve(dataSource, color) {
  return new LogCurve(dataSource).setLineStyle({
    "color": color,
    "width": 2
  });
}
function createDeviatedTrack(trackWidth, trajectory, logData1, logData2) {
  const deviatedTrackWidth = 2 * DEPTH_TRACK_WIDTH + 2 * DATA_TRACK_WIDTH;
  const devTrack = new DeviatedTrack({
    "leftdeviationvalue": LEFT_DEVIATION_VALUE,
    "rightdeviationvalue": RIGHT_DEVIATION_VALUE,
    "deviatedtrackwidth": deviatedTrackWidth,
    "deviatedtrackoffset": 0,
    "deviation": {
      "trajectory": trajectory,
      "transformer": new PiecewiseTransformer({
        "approxthreshold": 0.5,
        "approxthresholdmd": 0.5,
        "outlinemodelbboxadjust": false
      }),
      "clip": true,
      "webglrendering": true
    }
  }).setBounds(new Rect(0, 0, trackWidth, 0)).setLayoutStyle({ "width": trackWidth }, true).enableClipping(true);
  const stackedTrack = new StackedTrack();
  stackedTrack.addTrack(createDepthTrack(DEPTH_TRACK_WIDTH, true, false, "MD"));
  stackedTrack.addTrack(createLinearTrack(DATA_TRACK_WIDTH).addChild(createCurve(logData1, new RgbaColor(255, 162, 20))));
  stackedTrack.addTrack(createLinearTrack(DATA_TRACK_WIDTH).addChild(createCurve(logData2, new RgbaColor(111, 164, 217))));
  stackedTrack.addTrack(createDepthTrack(DEPTH_TRACK_WIDTH, false, true, "MD"));
  devTrack.addTrack(stackedTrack);
  return devTrack;
}
export class DepthBasedDeviation {
  constructor(options) {
    this._depthAutoScroll = false;
    this._timer = null;
    this._logData1 = new LogData(CURVE1_NAME);
    this._logData2 = new LogData(CURVE2_NAME);
    this._trajectory = new Trajectory2d();
    this._curveData1 = curveData[CURVE1_NAME];
    this._curveData2 = curveData[CURVE2_NAME];
    this._widget = this.createWidget();
    this._depthAutoScroll = true;
    this._plot = new Plot({
      "canvaselement": options.canvas,
      "autorootbounds": true,
      "root": this._widget
    });
    this.runData();
  }
  dispose() {
    if (this._timer != null) {
      clearInterval(this._timer);
    }
    if (this._plot != null) {
      this._plot.dispose();
    }
  }
  zoomIn() {
    this._widget.scale(defaults.zoomInScale);
  }
  zoomOut() {
    this._widget.scale(defaults.zoomOutScale);
  }
  fitToHeight() {
    this._widget.fitToHeight();
  }
  onLimitsChange(eventType, sender, args) {
    const maxVirtualDepth = this._widget.getDepthLimits().getHigh();
    const maxVisibleDepth = args["new"].getHigh();
    this._depthAutoScroll = maxVisibleDepth > maxVirtualDepth || MathUtil.equals(maxVisibleDepth, maxVirtualDepth);
  }
  createWidget() {
    const widget = createWellLogWidget({
      "scroll": {
        "headerverticalscroll": {
          "visible": true,
          "options": { "resizable": false }
        },
        "trackverticalscroll": {
          "visible": true,
          "options": { "resizable": false }
        }
      }
    }).setAxisHeaderType(HeaderType.Simple).scale(3);
    let index = widget.addTrack(TrackType.IndexTrack);
    widget.setTrackOptions(index, { "axis": { "name": "TVD" }, "autolabelrotation": false });
    const devTrack = createDeviatedTrack(DEV_TRACK_WIDTH, this._trajectory, this._logData1, this._logData2);
    widget.addTrack(devTrack);
    index = widget.addTrack(TrackType.IndexTrack);
    widget.setTrackOptions(index, { "axis": { "name": "TVD" }, "autolabelrotation": false });
    widget.setDepthLimits(START_DEPTH - TOP_MARGIN, START_DEPTH);
    widget.on(WellLogWidgetEvents.VisibleDepthLimitsChanged, this.onLimitsChange.bind(this));
    return widget;
  }
  runData() {
    let index = -1;
    let minTvd = Number.POSITIVE_INFINITY;
    let maxTvd = Number.NEGATIVE_INFINITY;
    const length = Math.min(trajectoryData.length / 3, this._curveData1.length, this._curveData2.length);
    const updateData = () => {
      const currSampleIndex = index < 0 ? 0 : Math.ceil(index / 3);
      if (currSampleIndex >= length) {
        this._trajectory.clear();
        this._logData1.clear();
        this._logData2.clear();
        this._widget.setDepthLimits(START_DEPTH - TOP_MARGIN, START_DEPTH);
        this._widget.scrollToIndex(START_DEPTH, ScrollToLocation.BOTTOM, false);
        minTvd = Number.POSITIVE_INFINITY;
        maxTvd = Number.NEGATIVE_INFINITY;
        index = -1;
      }
      const x = trajectoryData[++index];
      const y = trajectoryData[++index];
      const md = trajectoryData[++index];
      minTvd = Math.min(minTvd, y);
      maxTvd = Math.max(maxTvd, y);
      if (this._trajectory.count() >= MAX_CACHED_SIZE) {
        const minIdx = 0;
        const midIdx = Math.floor((this._trajectory.count() - 1) / 2);
        const minMD = this._trajectory.getDepth(minIdx);
        const midMD = this._trajectory.getDepth(midIdx);
        this._logData1.trimValues(minMD, midMD);
        this._logData2.trimValues(midMD, midMD);
        this._trajectory.shift(midIdx - minIdx);
      }
      this._logData1.addValue(md, this._curveData1[currSampleIndex]);
      this._logData2.addValue(md, this._curveData2[currSampleIndex]);
      this._trajectory.add(x, y, md);
      const wasSilent = this._widget.isSilent();
      this._widget.setSilent(true);
      this._widget.setDepthLimits(minTvd - TOP_MARGIN, maxTvd);
      this._widget.setSilent(wasSilent);
      if (this._depthAutoScroll) {
        this._widget.scrollToIndex(maxTvd, ScrollToLocation.BOTTOM, false);
      }
    };
    this._timer = setInterval(updateData, TIME_REFRESH);
  }
}

createScene();