{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["tabs","tab"]},"type":"markdown"},"seo":{"title":"Real-Time Deviated Track","description":"Accelerate E&P application development and protect your innovation by consuming our Data and Domain APIs / Platform APIs.","lang":"en-US","meta":[{"name":"robots","content":"noindex"}],"llmstxt":{"hide":true,"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"real-time-deviated-track","__idx":0},"children":["Real-Time Deviated Track"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The WellLog widget allows simulation of real-time flow monitoring. See the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/WellLog/Basics/WellLogWidget/wellLogWidget"},"children":["WellLog Widget tutorial."]},{"$$mdtype":"Tag","name":"br","attributes":{},"children":[]},"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 ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/WellLog/DataAndTemplates/RealTimeServer/realTimeServer"},"children":["Real-time Server"]}," tutorial."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For information on big data, see the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/WellLog/DataAndTemplates/BigData/bigData"},"children":["Big Data"]}," tutorial."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"depth-based-real-time-deviated-track","__idx":1},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/WellLog/DataAndTemplates/RealTimeDeviatedTrack/realTimeDeviatedTrack#depthBasedDeviation"},"children":["#"]}," Depth-based Real-time Deviated Track"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create a widget with deviated track and insert the widget in the plot and provide real-time data."]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[{"$$mdtype":"Tag","name":"div","attributes":{"label":"main","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ts","header":{"controls":{"copy":{}}},"source":"import { LogData } from \"@int/geotoolkit/welllog/data/LogData.ts\";\nimport { LogCurve } from \"@int/geotoolkit/welllog/LogCurve.ts\";\nimport { Plot } from \"@int/geotoolkit/plot/Plot.ts\";\nimport { Events as WellLogWidgetEvents } from \"@int/geotoolkit/welllog/widgets/Events.ts\";\nimport { HeaderType } from \"@int/geotoolkit/welllog/header/LogAxisVisualHeader.ts\";\nimport { TrackType } from \"@int/geotoolkit/welllog/TrackType.ts\";\nimport { ScrollToLocation } from \"@int/geotoolkit/welllog/TrackContainer.ts\";\nimport { defaults } from \"/src/helpers/defaults.js\";\nimport { Grid } from \"@int/geotoolkit/axis/Grid.ts\";\nimport { LogAxis } from \"@int/geotoolkit/welllog/LogAxis.ts\";\nimport { NumericLinearTickGenerator } from \"@int/geotoolkit/axis/NumericLinearTickGenerator.ts\";\nimport { LogTrack } from \"@int/geotoolkit/welllog/LogTrack.ts\";\nimport { RgbaColor } from \"@int/geotoolkit/util/RgbaColor.ts\";\nimport { Rect } from \"@int/geotoolkit/util/Rect.ts\";\nimport { Trajectory2d } from \"@int/geotoolkit/deviation/Trajectory2d.ts\";\nimport { trajectoryData } from \"/src/code/WellLog/DataAndTemplates/RealTimeDeviatedTrack/trajectory.ts\";\nimport { MathUtil } from \"@int/geotoolkit/util/MathUtil.ts\";\nimport { DeviatedTrack } from \"/src/code/WellLog/DataAndTemplates/RealTimeDeviatedTrack/DeviatedTrack.ts\";\nimport { PiecewiseTransformer } from \"@int/geotoolkit/deviation/PiecewiseTransformer.ts\";\nimport { StackedTrack } from \"@int/geotoolkit/welllog/StackedTrack.ts\";\nimport curveData from \"/src/helpers/curveData.json?import\";\nimport { createWellLogWidget } from \"/src/code/WellLog/utils/common.ts\";\nconst CURVE1_NAME = \"GR\";\nconst CURVE2_NAME = \"RHOB\";\nconst TIME_REFRESH = 200;\nconst TOP_MARGIN = 50;\nconst MAX_CACHED_SIZE = 400;\nconst START_DEPTH = trajectoryData[1];\nconst DATA_TRACK_WIDTH = 70;\nconst DEPTH_TRACK_WIDTH = 50;\nconst DEV_TRACK_WIDTH = 650;\nconst STEP_MAJOR = 100;\nconst LEFT_DEVIATION_VALUE = -50;\nconst RIGHT_DEVIATION_VALUE = 700;\nfunction createTrack(trackWidth) {\n  return new LogTrack().setLayoutStyle({ \"width\": trackWidth }, true).enableClipping(true).setLineStyle({\n    \"color\": \"#bdbdbd\",\n    \"pixelsnapmode\": true\n  });\n}\nfunction createDepthTrack(trackWidth, showLeftBorder, showRightBorder, name) {\n  const tgDepth = new NumericLinearTickGenerator().setTickStep(\"major\", STEP_MAJOR);\n  return createTrack(trackWidth).setBorders({ \"left\": showLeftBorder, \"right\": showRightBorder }).addChild(new LogAxis(tgDepth).setBaseLineStyle(null).setName(name));\n}\nfunction createLinearTrack(trackWidth) {\n  const tgValue = new NumericLinearTickGenerator().setTickStep(\"major\", 0.5).setTickStep(\"minor\", 0.25);\n  const tgDepth = new NumericLinearTickGenerator().setTickStep(\"major\", STEP_MAJOR);\n  return createTrack(trackWidth).addChild(new Grid(tgDepth, tgValue));\n}\nfunction createCurve(dataSource, color) {\n  return new LogCurve(dataSource).setLineStyle({\n    \"color\": color,\n    \"width\": 2\n  });\n}\nfunction createDeviatedTrack(trackWidth, trajectory, logData1, logData2) {\n  const deviatedTrackWidth = 2 * DEPTH_TRACK_WIDTH + 2 * DATA_TRACK_WIDTH;\n  const devTrack = new DeviatedTrack({\n    \"leftdeviationvalue\": LEFT_DEVIATION_VALUE,\n    \"rightdeviationvalue\": RIGHT_DEVIATION_VALUE,\n    \"deviatedtrackwidth\": deviatedTrackWidth,\n    \"deviatedtrackoffset\": 0,\n    \"deviation\": {\n      \"trajectory\": trajectory,\n      \"transformer\": new PiecewiseTransformer({\n        \"approxthreshold\": 0.5,\n        \"approxthresholdmd\": 0.5,\n        \"outlinemodelbboxadjust\": false\n      }),\n      \"clip\": true,\n      \"webglrendering\": true\n    }\n  }).setBounds(new Rect(0, 0, trackWidth, 0)).setLayoutStyle({ \"width\": trackWidth }, true).enableClipping(true);\n  const stackedTrack = new StackedTrack();\n  stackedTrack.addTrack(createDepthTrack(DEPTH_TRACK_WIDTH, true, false, \"MD\"));\n  stackedTrack.addTrack(createLinearTrack(DATA_TRACK_WIDTH).addChild(createCurve(logData1, new RgbaColor(255, 162, 20))));\n  stackedTrack.addTrack(createLinearTrack(DATA_TRACK_WIDTH).addChild(createCurve(logData2, new RgbaColor(111, 164, 217))));\n  stackedTrack.addTrack(createDepthTrack(DEPTH_TRACK_WIDTH, false, true, \"MD\"));\n  devTrack.addTrack(stackedTrack);\n  return devTrack;\n}\nexport class DepthBasedDeviation {\n  constructor(options) {\n    this._depthAutoScroll = false;\n    this._timer = null;\n    this._logData1 = new LogData(CURVE1_NAME);\n    this._logData2 = new LogData(CURVE2_NAME);\n    this._trajectory = new Trajectory2d();\n    this._curveData1 = curveData[CURVE1_NAME];\n    this._curveData2 = curveData[CURVE2_NAME];\n    this._widget = this.createWidget();\n    this._depthAutoScroll = true;\n    this._plot = new Plot({\n      \"canvaselement\": options.canvas,\n      \"autorootbounds\": true,\n      \"root\": this._widget\n    });\n    this.runData();\n  }\n  dispose() {\n    if (this._timer != null) {\n      clearInterval(this._timer);\n    }\n    if (this._plot != null) {\n      this._plot.dispose();\n    }\n  }\n  zoomIn() {\n    this._widget.scale(defaults.zoomInScale);\n  }\n  zoomOut() {\n    this._widget.scale(defaults.zoomOutScale);\n  }\n  fitToHeight() {\n    this._widget.fitToHeight();\n  }\n  onLimitsChange(eventType, sender, args) {\n    const maxVirtualDepth = this._widget.getDepthLimits().getHigh();\n    const maxVisibleDepth = args[\"new\"].getHigh();\n    this._depthAutoScroll = maxVisibleDepth > maxVirtualDepth || MathUtil.equals(maxVisibleDepth, maxVirtualDepth);\n  }\n  createWidget() {\n    const widget = createWellLogWidget({\n      \"scroll\": {\n        \"headerverticalscroll\": {\n          \"visible\": true,\n          \"options\": { \"resizable\": false }\n        },\n        \"trackverticalscroll\": {\n          \"visible\": true,\n          \"options\": { \"resizable\": false }\n        }\n      }\n    }).setAxisHeaderType(HeaderType.Simple).scale(3);\n    let index = widget.addTrack(TrackType.IndexTrack);\n    widget.setTrackOptions(index, { \"axis\": { \"name\": \"TVD\" }, \"autolabelrotation\": false });\n    const devTrack = createDeviatedTrack(DEV_TRACK_WIDTH, this._trajectory, this._logData1, this._logData2);\n    widget.addTrack(devTrack);\n    index = widget.addTrack(TrackType.IndexTrack);\n    widget.setTrackOptions(index, { \"axis\": { \"name\": \"TVD\" }, \"autolabelrotation\": false });\n    widget.setDepthLimits(START_DEPTH - TOP_MARGIN, START_DEPTH);\n    widget.on(WellLogWidgetEvents.VisibleDepthLimitsChanged, this.onLimitsChange.bind(this));\n    return widget;\n  }\n  runData() {\n    let index = -1;\n    let minTvd = Number.POSITIVE_INFINITY;\n    let maxTvd = Number.NEGATIVE_INFINITY;\n    const length = Math.min(trajectoryData.length / 3, this._curveData1.length, this._curveData2.length);\n    const updateData = () => {\n      const currSampleIndex = index < 0 ? 0 : Math.ceil(index / 3);\n      if (currSampleIndex >= length) {\n        this._trajectory.clear();\n        this._logData1.clear();\n        this._logData2.clear();\n        this._widget.setDepthLimits(START_DEPTH - TOP_MARGIN, START_DEPTH);\n        this._widget.scrollToIndex(START_DEPTH, ScrollToLocation.BOTTOM, false);\n        minTvd = Number.POSITIVE_INFINITY;\n        maxTvd = Number.NEGATIVE_INFINITY;\n        index = -1;\n      }\n      const x = trajectoryData[++index];\n      const y = trajectoryData[++index];\n      const md = trajectoryData[++index];\n      minTvd = Math.min(minTvd, y);\n      maxTvd = Math.max(maxTvd, y);\n      if (this._trajectory.count() >= MAX_CACHED_SIZE) {\n        const minIdx = 0;\n        const midIdx = Math.floor((this._trajectory.count() - 1) / 2);\n        const minMD = this._trajectory.getDepth(minIdx);\n        const midMD = this._trajectory.getDepth(midIdx);\n        this._logData1.trimValues(minMD, midMD);\n        this._logData2.trimValues(midMD, midMD);\n        this._trajectory.shift(midIdx - minIdx);\n      }\n      this._logData1.addValue(md, this._curveData1[currSampleIndex]);\n      this._logData2.addValue(md, this._curveData2[currSampleIndex]);\n      this._trajectory.add(x, y, md);\n      const wasSilent = this._widget.isSilent();\n      this._widget.setSilent(true);\n      this._widget.setDepthLimits(minTvd - TOP_MARGIN, maxTvd);\n      this._widget.setSilent(wasSilent);\n      if (this._depthAutoScroll) {\n        this._widget.scrollToIndex(maxTvd, ScrollToLocation.BOTTOM, false);\n      }\n    };\n    this._timer = setInterval(updateData, TIME_REFRESH);\n  }\n}\n\ncreateScene();\n\n","lang":"ts"},"children":[]}]},{"$$mdtype":"Tag","name":"div","attributes":{"label":"css","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"css","header":{"controls":{"copy":{}}},"source":"\n.cg-tooltip-holder {\n  position: relative;\n}\n\n.cg-tooltip {\n  position: absolute;\n  display: block;\n  padding: 2px 12px 3px 7px;\n  overflow: visible !important;\n  font-family: Roboto, Helvetica, Arial, sans-serif;\n  font-size: 13px;\n  background: white !important;\n  opacity: 0.9;\n  color: #333333;\n  border: solid 1px gray;\n  border-radius: 5px;\n  text-align: left;\n  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);\n  border-radius: 5px;\n  margin: 0 !important;\n  z-index: 10000;\n  max-width: 400px;\n  text-wrap: normal !important;\n  white-space: normal !important;\n}\n/* Default setting for tooltip */\n.cg-tooltip-container {\n  position: absolute;\n  display: block;\n  overflow: visible !important;\n  font-family: Roboto, Helvetica, Arial, sans-serif;\n  font-size: 12px;\n  padding: 3px 7px;\n  background: #f7f7f7;\n  color: #333333;\n  border: 1px solid #938e8e;\n  opacity: 0.8;\n  text-align: left;\n  box-shadow: 3px 3px 10px #888;\n  margin: 0 !important;\n  z-index: 10000;\n  max-width: 400px;\n  text-wrap: normal !important;\n  white-space: normal !important;\n  user-select: none;\n}\n@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {\n  .cg-tooltip-container {\n    border-radius: 0;\n  }\n}\n/* Default left arrow for tooltip */\n.cg-tooltip-arrow-left::before {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 0px;\n  left: 0;\n  top: 50%;\n  border: 5px solid transparent;\n  border-left: 0;\n  border-right: 5px solid  #938e8e;\n  transform: translate(calc(-100%), -50%);\n}\n.cg-tooltip-arrow-left::after {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 0px;\n  left: 0;\n  top: 50%;\n  border: 4px solid transparent;\n  border-left: 0;\n  border-right: 4px solid #f7f7f7;\n  transform: translate(calc(-100%), -50%);\n}\n/* Default top arrow for tooltip */\n.cg-tooltip-arrow-top::before {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 0px;\n  left: 50%;\n  top: 0;\n  border: 5px solid transparent;\n  border-top: 0;\n  border-bottom: 5px solid #938e8e;\n  transform: translate(-50%, -100%);\n}\n.cg-tooltip-arrow-top::after {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 0px;\n  left: 50%;\n  top: 0;\n  border: 4px solid transparent;\n  border-top: 0;\n  border-bottom: 4px solid #f7f7f7;\n  transform: translate(-50%, -100%);\n}\n/* Default right arrow for tooltip */\n.cg-tooltip-arrow-right::before {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 0px;\n  right: 0;\n  top: 50%;\n  border: 5px solid transparent;\n  border-right: 0;\n  border-left: 5px solid #938e8e;\n  transform: translate(100%, -50%);\n}\n.cg-tooltip-arrow-right::after {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 0px;\n  right: 0;\n  top: 50%;\n  border: 4px solid transparent;\n  border-right: 0;\n  border-left: 4px solid #f7f7f7;\n  transform: translate(100%, -50%);\n}\n/* Default bottom arrow for tooltip */\n.cg-tooltip-arrow-bottom::before {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 0px;\n  left: 50%;\n  bottom: 0px;\n  border: 5px solid transparent;\n  border-bottom: 0;\n  border-top: 5px solid #938e8e;\n  transform: translate(-50%, 100%);\n  z-index: 10000;\n}\n.cg-tooltip-arrow-bottom::after {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 0px;\n  left: 50%;\n  bottom: 0;\n  border: 4px solid transparent;\n  border-bottom: 0;\n  border-top: 4px solid #f7f7f7;\n  transform: translate(-50%, 100%);\n  z-index: 10000;\n}\n/* Tooltip item name */\n/* Tooltip item value */\n/* .cg-tooltip-item-value */\n/* Tooltip item value */\n.cg-tooltip-item-unit {\n  text-transform: none;\n}\n\n.cg-tooltip-item-name {\n    text-transform: capitalize;\n    white-space: nowrap;\n    vertical-align: middle;\n    font-size: 13px;\n}\n.cg-tooltip-row {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  white-space: pre-wrap;\n  font-size: 12px;\n  line-height: 100%;\n  margin: 1px 0;\n}\n.cg-tooltip-title {\n  font-size: 13px;\n  height: 14px;\n  text-transform: capitalize;\n}\n.cg-tooltip-title .cg-tooltip-symbol {\n  margin-right: 0 !important;\n}\n.cg-tooltip-title-name {\n  vertical-align: middle;\n}\n.cg-tooltip-row + .cg-tooltip-row {\n  margin-top: 4px;\n}\n.cg-tooltip-row.cg-tooltip-title + .cg-tooltip-row {\n  margin-top: 5px;\n}\n.cg-tooltip-item-value + .cg-tooltip-item-unit {\n    margin-left: 1px;\n}\n/* Tooltip symbol */\n.cg-tooltip-symbol-cell {\n  display: inline-flex;\n  min-width: 13px; /* 10px size + 3px margin */\n}\n.cg-tooltip-symbol {\n  margin-right: 3px;\n  background-color: transparent;\n  display: block;\n}\n.cg-tooltip-symbol > img {\n  display: block;\n}\n.cg-tooltip-list-cell {\n  display: inline-flex;\n}\n.cg-tooltip-list-symbol {\n  display: block;\n  margin-right: 3px;\n  width: 6px;\n  height: 6px;\n  vertical-align: middle;\n  border-radius: 50%;\n  border: 1px solid rgba(0,0,0,.4);\n}\n.cg-tooltip-symbol-legacy {\n  border-radius: 4px;\n  margin-right: 5px;\n  height: 8px;\n  width: 8px;\n  display: inline-block;\n}\n.cg-tooltip-title-legacy {\n  font-weight: 900;\n}\n\n/* Tooltip symbol circle */\n.cg-tooltip-symbol.circle {\n  height: 10px;\n  width: 10px;\n  display: inline-block;\n  border-radius: 50%;\n  border: 1px solid rgba(0,0,0,.4);\n}\n/* Tooltip symbol line */\n.cg-tooltip-symbol.line {\n    height: 10px;\n    width: 10px;\n    display: inline-block;\n    transform: scale(1.2, 0.2);\n}\n/* Tooltip symbol diamond */\n.cg-tooltip-symbol.diamond {\n    height: 10px;\n    width: 10px;\n    display: inline-block;\n    transform: rotate(45deg);\n    border-radius: 0px;\n}\n/* Tooltip symbol square */\n.cg-tooltip-symbol.square {\n    height: 10px;\n    width: 10px;\n    display: inline-block;\n    border-radius: 0px;\n    border: 1px solid rgba(0,0,0,.4);\n}\n\n","lang":"css"},"children":[]}]}]},{"$$mdtype":"Tag","name":"iframe","attributes":{"src":"https://dc2-documentation.s3.amazonaws.com/documentation/slb-docs-test/5.0/examples/vue/tutorials/index.html#/WellLog/DataAndTemplates/RealTimeDeviatedTrack/realTimeDeviatedTrack?section=depthBasedDeviation&extract=true","width":"100%","height":"542.5px","style":{"border":"none"}},"children":[]}]},"headings":[{"value":"Real-Time Deviated Track","id":"real-time-deviated-track","depth":1},{"value":"Depth-based Real-time Deviated Track","id":"depth-based-real-time-deviated-track","depth":3}],"frontmatter":{"title":"Real-Time Deviated Track","seo":{"title":"Real-Time Deviated Track"}},"lastModified":"2026-02-11T19:54:32.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/solutions/geotoolkit/tutorials/well-log/data-and-templates/real-time-deviated-track","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}