{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["tabs","tab"]},"type":"markdown"},"seo":{"title":"Real-Time Deviated Schematics","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-schematics","__idx":0},"children":["Real-Time Deviated Schematics"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This tutorial demonstrates how to use the Deviated Schematics Widget for real-time data visualization. The Deviated Schematics Widget is derived from ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/doc/classes/geotoolkit.widgets.annotatedwidget.annotatedwidget.html"},"children":["geotoolkit/widgets/AnnotatedWidget"]}," and uses ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RealTimeDeviatedSchematics/data/DynamicWellBoreData"]}," that extends the supplied ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/doc/classes/geotoolkit.schematics.data.wellboredata.wellboredata.html"},"children":["geotoolkit/schematics/data/WellBoreData"]}," as the data model. Familiarization with ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/Schematics/Basics/basics"},"children":["Schematics - Basics"]}," and ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/Schematics/DeviatedSchematicsWidget/deviatedSchematicsWidget"},"children":["Schematics - DeviatedSchematicsWidget"]}," tutorials will be beneficial."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"widget-initialization","__idx":1},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/Schematics/RealTimeDeviatedSchematics/realTimeDeviatedSchematics#widgetInitialization"},"children":["#"]}," Widget Initialization"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Widget initialization is similar to a deviated schematic widget. First, create a new instance of ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/doc/classes/geotoolkit.schematics.widgets.deviatedschematicswidget.deviatedschematicswidget.html"},"children":["geotoolkit/schematics/widgets/DeviatedSchematicWidget"]},". The only required parameter is the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]}," object. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]}," includes the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["elements"]}," property that contains the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DynamicWellBoreData"]}," and the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["trajectory"]}," property that provides data on how the well deviates."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["After creating the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DeviatedSchematicWidget"]}," object, instantiate a new ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/doc/classes/geotoolkit.plot.plot.plot.html"},"children":["geotoolkit/plot/Plot"]}," instance and add the widget as a root."]},{"$$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 { Axis } from \"@int/geotoolkit/axis/Axis.ts\";\nimport { NumberFormat } from \"@int/geotoolkit/util/NumberFormat.ts\";\nimport { from } from \"@int/geotoolkit/selection/from.ts\";\nimport { Plot } from \"@int/geotoolkit/plot/Plot.ts\";\nimport { CompositeSchematicsWidget, DisplayMode } from \"@int/geotoolkit/schematics/widgets/CompositeSchematicsWidget.ts\";\nimport { LocationType } from \"@int/geotoolkit/schematics/labeling/LocationType.ts\";\nimport { Trajectory2d } from \"@int/geotoolkit/deviation/Trajectory2d.ts\";\nimport { ViewMode } from \"@int/geotoolkit/schematics/scene/WellBoreNode.ts\";\nimport { Rect } from \"@int/geotoolkit/util/Rect.ts\";\nimport { SVGComponentsLoader } from \"@int/geotoolkit/schematics/factory/SVGComponentsLoader.ts\";\nimport { ComponentNodeFactoryRegistry } from \"@int/geotoolkit/schematics/factory/ComponentNodeFactoryRegistry.ts\";\nimport trajectoryData from \"/src/code/Schematics/RealTimeDeviatedSchematics/data/payload.json?import\";\nimport { data as wbData } from \"/src/code/Schematics/RealTimeDeviatedSchematics/data/wellBoreData.ts\";\nimport { deepMergeObject } from \"@int/geotoolkit/base.js\";\nimport { MathUtil } from \"@int/geotoolkit/util/MathUtil.ts\";\nimport { DynamicWellBoreData } from \"/src/code/Schematics/RealTimeDeviatedSchematics/data/DynamicWellBoreData.ts\";\nimport { TableView } from \"@int/geotoolkit/widgets/TableView.ts\";\nimport { DataTable } from \"@int/geotoolkit/data/DataTable.ts\";\nimport { DataTableAdapter } from \"@int/geotoolkit/widgets/data/DataTableAdapter.ts\";\nimport { Dimension } from \"@int/geotoolkit/util/Dimension.ts\";\nimport { Group } from \"@int/geotoolkit/scene/Group.ts\";\nimport { FlexboxLayout, FlexDirection } from \"@int/geotoolkit/layout/FlexboxLayout.ts\";\nconst TIME_REFRESH = 300;\nconst SCALE_FACTOR = 1.2;\nconst AUTO_SCROLL_THRESHOLD = 100;\nconst ROUND_ACCURACY_BASE = 100;\nfunction round(value) {\n  return MathUtil.round(value, ROUND_ACCURACY_BASE);\n}\nfunction updateSchematicsWidget(widget, nextX, nextY, nextMD) {\n  const deviation = widget.getDeviation();\n  if (deviation == null || deviation[\"trajectory\"] == null) {\n    return;\n  }\n  const trajectory = deviation[\"trajectory\"];\n  const wbData2 = widget.getData();\n  if (wbData2 instanceof DynamicWellBoreData) {\n    const depthRange = {\n      \"from\": trajectory.minDepth(),\n      \"to\": nextMD\n    };\n    const trajectoryUnits = trajectory.getProperties()[\"options\"][\"units\"];\n    if (trajectoryUnits != null && trajectoryUnits[\"distance\"] != null) {\n      depthRange[\"units\"] = trajectoryUnits[\"distance\"];\n    }\n    wbData2.update(depthRange);\n  }\n  trajectory.add(nextX, nextY, nextMD);\n}\nfunction resetSchematicsWidget(widget, data) {\n  const wbData2 = widget.getData();\n  if (wbData2 != null) {\n    wbData2.removeAll().addComponents(data[\"elements\"].getProperties()[\"elements\"]);\n  }\n  const deviation = widget.getDeviation();\n  const trajectory = deviation != null ? deviation[\"trajectory\"] : null;\n  if (trajectory != null) {\n    trajectory.clear().add(data[\"trajectory\"].getArrayX(), data[\"trajectory\"].getArrayY(), data[\"trajectory\"].getDepths());\n  }\n  if (wbData2 == null || trajectory == null) {\n    widget.setData(data);\n  }\n}\nfunction updateTrajectoryTable(widget, nextX, nextY, nextMD) {\n  const provider = widget.getTableViewShape().getDataProvider();\n  if (!(provider instanceof DataTableAdapter)) {\n    return;\n  }\n  const dataTable = provider.getDataTable();\n  if (dataTable == null) {\n    return;\n  }\n  dataTable.addRow([\n    round(nextX),\n    round(nextY),\n    round(nextMD)\n  ]);\n  widget.setData({\n    \"rows\": dataTable.getNumberOfRows(),\n    \"cols\": dataTable.getNumberOfColumns()\n  });\n}\nfunction resetTrajectoryTable(widget, trajectory) {\n  const provider = widget.getTableViewShape().getDataProvider();\n  if (!(provider instanceof DataTableAdapter)) {\n    return;\n  }\n  const dataTable = provider.getDataTable();\n  if (dataTable == null) {\n    return;\n  }\n  dataTable.clear().setColumnValues(0, trajectory.getArrayX().map((value) => round(value))).setColumnValues(1, trajectory.getArrayY().map((value) => round(value))).setColumnValues(2, trajectory.getDepths().map((value) => round(value)));\n  widget.setData({\n    \"rows\": dataTable.getNumberOfRows(),\n    \"cols\": dataTable.getNumberOfColumns()\n  });\n}\nfunction createData() {\n  const staticData = [];\n  const dynamicData = [];\n  wbData.forEach((element) => {\n    if (element[\"name\"] === \"casing\" || element[\"name\"] === \"cement\") {\n      staticData.push(element);\n    } else {\n      const dynamicElement = createDynamicElement(element);\n      dynamicData.push(dynamicElement);\n    }\n  });\n  const wellBoreData = new DynamicWellBoreData().addComponents([...staticData, ...dynamicData]);\n  const trajectory = createInitialTrajectory(wellBoreData);\n  const initDepthRange = {\n    \"from\": trajectory.minDepth(),\n    \"to\": trajectory.maxDepth()\n  };\n  const trajectoryUnits = trajectory.getProperties()[\"options\"][\"units\"];\n  if (trajectoryUnits != null && trajectoryUnits[\"distance\"] != null) {\n    initDepthRange[\"units\"] = trajectoryUnits[\"distance\"];\n  }\n  wellBoreData.update(initDepthRange);\n  return {\n    \"elements\": wellBoreData,\n    \"trajectory\": trajectory\n  };\n}\nfunction createDynamicElement(staticElement) {\n  let updateMethod;\n  if (staticElement[\"name\"].toLowerCase().includes(\"pipe\")) {\n    updateMethod = (el, depthRange) => {\n      const geometry2 = el[\"geometry\"];\n      if (geometry2[\"depth\"] == null || depthRange[\"to\"] == null) {\n        return;\n      }\n      geometry2[\"depth\"][\"to\"] = depthRange[\"to\"];\n      el[\"extent\"] = geometry2[\"depth\"][\"to\"] - geometry2[\"depth\"][\"from\"];\n    };\n  } else {\n    updateMethod = (el, depthRange) => {\n      const geometry2 = el[\"geometry\"];\n      if (el[\"extent\"] == null || geometry2[\"depth\"] == null || depthRange[\"to\"] == null) {\n        return;\n      }\n      geometry2[\"depth\"][\"to\"] = depthRange[\"to\"];\n      geometry2[\"depth\"][\"from\"] = geometry2[\"depth\"][\"to\"] - el[\"extent\"];\n    };\n  }\n  const dynamicElement = deepMergeObject(staticElement, {\n    \"data\": {\n      \"geometry\": {},\n      \"updatemethod\": updateMethod\n    }\n  });\n  const geometry = Array.isArray(dynamicElement[\"data\"][\"geometry\"]) ? dynamicElement[\"data\"][\"geometry\"][0] : dynamicElement[\"data\"][\"geometry\"];\n  if (geometry[\"depth\"] != null) {\n    dynamicElement[\"data\"][\"extent\"] = geometry[\"depth\"][\"to\"] - geometry[\"depth\"][\"from\"];\n  }\n  return dynamicElement;\n}\nfunction createInitialTrajectory(wellBoreData) {\n  const geometryBounds = wellBoreData.getStaticGeometryBounds() || wellBoreData.getDynamicGeometryBounds();\n  if (geometryBounds == null) {\n    return new Trajectory2d();\n  }\n  const maxDepth = geometryBounds.getBottom();\n  const trajIdx = trajectoryData.items.findIndex((item) => item.WAS_MD >= maxDepth);\n  if (trajIdx < 0) {\n    throw new Error();\n  }\n  let lastStaticMD;\n  let lastStaticTVD;\n  let lastStaticVS;\n  const currItem = trajectoryData.items[trajIdx];\n  if (trajIdx > 0 && trajIdx < trajectoryData.items.length - 1 && currItem.WAS_MD !== maxDepth) {\n    const prevItem = trajectoryData.items[trajIdx - 1];\n    const mdRatio = (maxDepth - prevItem.WAS_MD) / (currItem.WAS_MD - prevItem.WAS_MD);\n    lastStaticMD = maxDepth;\n    lastStaticTVD = prevItem.WAS_TVD + (currItem.WAS_TVD - prevItem.WAS_TVD) * mdRatio;\n    lastStaticVS = prevItem.WAS_VS + (currItem.WAS_VS - prevItem.WAS_VS) * mdRatio;\n  } else {\n    lastStaticMD = currItem.WAS_MD;\n    lastStaticTVD = currItem.WAS_TVD;\n    lastStaticVS = currItem.WAS_VS;\n  }\n  const trajData = readTrajectory(trajIdx);\n  trajData.d.push(lastStaticMD);\n  trajData.x.push(lastStaticVS);\n  trajData.y.push(lastStaticTVD);\n  return new Trajectory2d({\n    \"data\": {\n      \"x\": trajData.x,\n      \"y\": trajData.y,\n      \"d\": trajData.d\n    }\n  });\n}\nfunction readTrajectory(stationCount) {\n  const d = [];\n  const x = [];\n  const y = [];\n  for (let i = 0; i < stationCount; i++) {\n    const currItem = trajectoryData.items[i];\n    d.push(currItem.WAS_MD);\n    x.push(currItem.WAS_VS);\n    y.push(currItem.WAS_TVD);\n  }\n  return { x, y, d };\n}\nfunction setDataLimits(widget) {\n  const fullTrajectory = new Trajectory2d({\n    \"data\": readTrajectory(trajectoryData.items.length)\n  });\n  const entireSceneArea = new Rect(\n    fullTrajectory.getMinX(),\n    fullTrajectory.getMinY(),\n    fullTrajectory.getMaxX(),\n    fullTrajectory.getMaxY()\n  );\n  widget.setDeviation({\n    \"datamodellimits\": entireSceneArea\n  });\n}\nfunction createTrajectoryTable(trajectory) {\n  const rows = [];\n  for (let i = 0; i < trajectory.count(); i++) {\n    rows.push([\n      round(trajectory.getX(i)),\n      round(trajectory.getY(i)),\n      round(trajectory.getDepth(i))\n    ]);\n  }\n  const dataTable = new DataTable({\n    cols: [\n      { name: \"X\", type: \"number\" },\n      { name: \"Y\", type: \"number\" },\n      { name: \"MD\", type: \"number\" }\n    ],\n    rowsdata: rows\n  });\n  const bridge = new DataTableAdapter({\n    \"datatable\": dataTable\n  });\n  const tableViewWidget = new TableView({\n    \"verticalscroll\": \"floating\",\n    \"horizontalscroll\": \"floating\"\n  }).setData({\n    \"defaultcellsize\": new Dimension(233, 25),\n    \"indextitle\": \"Index\",\n    \"dataprovider\": bridge,\n    \"rows\": dataTable.getNumberOfRows(),\n    \"cols\": dataTable.getNumberOfColumns()\n  });\n  const splitterTool = tableViewWidget.getToolByName(\"TableViewSplitter\");\n  if (splitterTool != null) {\n    splitterTool.setEnabled(false);\n  }\n  return tableViewWidget.setVisibleTableLimits(tableViewWidget.getTableSize().getHeight() - 1);\n}\nfunction createScene(canvas) {\n  const registrySVG = new ComponentNodeFactoryRegistry(true);\n  const loader = new SVGComponentsLoader({\n    \"registry\": registrySVG,\n    \"path\": RESOURCES + \"svg/components.json\"\n  });\n  loader.load().then(() => {\n    const registryJS = new ComponentNodeFactoryRegistry(true);\n    registrySVG.setFactory(\"casing\", registryJS.getFactory(\"casing\"));\n    registrySVG.setFactory(\"cement\", registryJS.getFactory(\"cement\"));\n  });\n  const data = createData();\n  const options = {\n    \"wellborenode\": {\n      \"registry\": registrySVG\n    },\n    \"north\": {\n      \"title\": {\n        \"text\": \"Deviated Schematics Widget\"\n      }\n    },\n    \"gap\": {\n      \"left\": {\n        \"visible\": true,\n        \"size\": \"180px\",\n        \"resizable\": false\n      },\n      \"right\": {\n        \"visible\": true,\n        \"size\": \"100px\",\n        \"resizable\": false\n      },\n      \"top\": {\n        \"visible\": true,\n        \"size\": \"50px\",\n        \"resizable\": false\n      },\n      \"bottom\": {\n        \"visible\": true,\n        \"size\": \"50px\",\n        \"resizable\": false\n      }\n    },\n    \"deviation\": {\n      \"trackwidth\": 30\n    },\n    \"trajectory\": {\n      \"stations\": {\n        \"visible\": false\n      },\n      \"lines\": {\n        \"visible\": true,\n        \"linestyle\": { \"color\": \"blue\" }\n      }\n    },\n    \"labeling\": {\n      \"defaultlocation\": LocationType.Auto\n    },\n    \"data\": createData()\n  };\n  const widget = new CompositeSchematicsWidget(options);\n  setDataLimits(widget);\n  widget.setViewMode(ViewMode.Regular);\n  widget.setDisplayMode(DisplayMode.Deviated);\n  from(widget).where((child) => child instanceof Axis).selectToArray().forEach((axis) => {\n    axis.getTickGenerator().setLabelFormat(\"major\", new NumberFormat({ \"maximumfractiondigits\": 0 }));\n  });\n  const trajectoryTable = createTrajectoryTable(data.trajectory);\n  widget.setLayoutStyle({\n    \"width\": 800,\n    \"height\": 600\n  });\n  trajectoryTable.setLayoutStyle({\n    \"width\": 800,\n    \"height\": 200\n  });\n  const rootGroup = new Group({\n    \"children\": [widget, trajectoryTable],\n    \"layout\": new FlexboxLayout({\n      \"flexdirection\": FlexDirection.Column\n    })\n  });\n  return new Plot({\n    \"canvaselement\": canvas,\n    \"root\": rootGroup\n  });\n}\nclass RealTimeDeviatedSchematicsDemo {\n  constructor(canvas) {\n    this._plot = createScene(canvas);\n    this._widget = from(this._plot.getRoot()).where((item) => item instanceof CompositeSchematicsWidget).selectFirst();\n    this._trajectoryTable = from(this._plot.getRoot()).where((item) => item instanceof TableView).selectFirst();\n    this._timer = null;\n    this._isStarted = false;\n    this._isSuspended = false;\n    this._isAutoScrollEnabled = true;\n  }\n  getPlot() {\n    return this._plot;\n  }\n  zoomIn() {\n    this._widget.scaleModel(SCALE_FACTOR, SCALE_FACTOR, 0, 0);\n    if (this.isAutoScrollEnabled()) {\n      this.adjustAutoScrollPosition();\n    }\n  }\n  zoomOut() {\n    this._widget.scaleModel(1 / SCALE_FACTOR, 1 / SCALE_FACTOR, 0, 0);\n    if (this.isAutoScrollEnabled()) {\n      this.adjustAutoScrollPosition();\n    }\n  }\n  zoomToFit() {\n    this._widget.fitToBounds();\n  }\n  start() {\n    if (this.isStarted() && !this.isSuspended()) {\n      return;\n    }\n    this._timer = setInterval(this.updateData.bind(this), TIME_REFRESH);\n    this._isStarted = true;\n    this._isSuspended = false;\n  }\n  isStarted() {\n    return this._isStarted;\n  }\n  toggleSuspend() {\n    if (this.isSuspended()) {\n      this._timer = setInterval(this.updateData.bind(this), TIME_REFRESH);\n    } else if (this._timer != null) {\n      clearInterval(this._timer);\n    }\n    this._isSuspended = !this._isSuspended;\n  }\n  isSuspended() {\n    return this._isSuspended;\n  }\n  stop() {\n    this._isStarted = false;\n    this._isSuspended = false;\n    if (this._timer != null) {\n      clearInterval(this._timer);\n    }\n    this._timer = null;\n    this.resetData();\n    this.adjustAutoScrollPosition();\n  }\n  toggleAutoScroll() {\n    this._isAutoScrollEnabled = !this._isAutoScrollEnabled;\n  }\n  isAutoScrollEnabled() {\n    return this._isAutoScrollEnabled;\n  }\n  dispose() {\n    this.stop();\n    this._plot.dispose(true);\n  }\n  updateData() {\n    const deviation = this._widget.getDeviation();\n    if (deviation == null || deviation[\"trajectory\"] == null) {\n      return;\n    }\n    const trajectory = deviation[\"trajectory\"];\n    const currDepth = trajectory.maxDepth();\n    const nextSampleIdx = trajectoryData.items.findIndex((item) => item.WAS_MD > currDepth);\n    if (nextSampleIdx > 0) {\n      const nextSample = trajectoryData.items[nextSampleIdx];\n      const nextX = nextSample.WAS_VS;\n      const nextY = nextSample.WAS_TVD;\n      const nextMD = nextSample.WAS_MD;\n      updateSchematicsWidget(this._widget, nextX, nextY, nextMD);\n      updateTrajectoryTable(this._trajectoryTable, nextX, nextY, nextMD);\n    } else {\n      this.resetData();\n    }\n    if (this.isAutoScrollEnabled()) {\n      this.adjustAutoScrollPosition();\n    }\n  }\n  resetData() {\n    const initData = createData();\n    resetSchematicsWidget(this._widget, initData);\n    resetTrajectoryTable(this._trajectoryTable, initData[\"trajectory\"]);\n  }\n  adjustAutoScrollPosition() {\n    const trajectory = this._widget.getDeviation() != null ? this._widget.getDeviation()[\"trajectory\"] : null;\n    if (trajectory == null) {\n      return;\n    }\n    const bounds = this._widget.getModel().getVisibleDeviceLimits();\n    if (bounds == null) {\n      return;\n    }\n    const cnt = trajectory.count();\n    let posX = trajectory.getX(cnt - 1);\n    let posY = trajectory.getY(cnt - 1);\n    const sceneTr = this._widget.getWellBoreWithLabels().getSceneTransform();\n    if (sceneTr != null && !sceneTr.isIdentity()) {\n      const devicePos = sceneTr.transformXY(posX, posY);\n      posX = devicePos.getX();\n      posY = devicePos.getY();\n    }\n    const left = bounds.getLeft() + AUTO_SCROLL_THRESHOLD - posX;\n    const right = posX - (bounds.getRight() - AUTO_SCROLL_THRESHOLD);\n    const top = bounds.getTop() + AUTO_SCROLL_THRESHOLD - posY;\n    const bottom = posY - (bounds.getBottom() - AUTO_SCROLL_THRESHOLD);\n    const dx = left > right ? Math.max(left, 0) : -Math.max(right, 0);\n    const dy = top > bottom ? Math.max(top, 0) : -Math.max(bottom, 0);\n    if (!MathUtil.equals(dx, 0) || !MathUtil.equals(dy, 0)) {\n      this._widget.translateModel(dx, dy);\n    }\n    this._trajectoryTable.setVisibleTableLimits(this._trajectoryTable.getTableSize().getHeight() - 1);\n  }\n}\nexport { RealTimeDeviatedSchematicsDemo };\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#/Schematics/RealTimeDeviatedSchematics/realTimeDeviatedSchematics?section=widgetInitialization&extract=true","width":"100%","height":"948.5px","style":{"border":"none"}},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"data-initialization","__idx":2},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/Schematics/RealTimeDeviatedSchematics/realTimeDeviatedSchematics#dataInitialization"},"children":["#"]}," Data Initialization"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Two objects are required to instantiate an instance of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DeviatedSchematicsWidget"]},":"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["An ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["elements"]}," object, which is an object containing ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/doc/classes/geotoolkit.schematics.data.wellboredata.wellboredata.html"},"children":["geotoolkit/schematics/data/WellBoreData"]}," to draw the well schematic. Here we will use data from a separate file, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TESTWELLBOREDATA.ts"]},". First, we need to sort elements into static and dynamic ones, then specify update method for dynamic elements and finally, create a new ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DynamicWellBoreData"]}," object."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["trajectory"]}," object created by the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/doc/classes/geotoolkit.deviation.trajectory2d.trajectory2d.html"},"children":["geotoolkit/deviation/Trajectory2d"]}," constructor."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note"]}," that we need to set data model limits for entire deviated scene in order to make layout properly (see ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["setDataLimits"]}," method for details)."]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"data-updating","__idx":3},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/Schematics/RealTimeDeviatedSchematics/realTimeDeviatedSchematics#dataUpdating"},"children":["#"]}," Data Updating"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To update ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DeviatedSchematicsWidget"]}," with incoming data we need to update both well bore data and trajectory."]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[]}]},"headings":[{"value":"Real-Time Deviated Schematics","id":"real-time-deviated-schematics","depth":1},{"value":"Widget Initialization","id":"widget-initialization","depth":3},{"value":"Data Initialization","id":"data-initialization","depth":3},{"value":"Data Updating","id":"data-updating","depth":3}],"frontmatter":{"title":"Real-Time Deviated Schematics","seo":{"title":"Real-Time Deviated Schematics"}},"lastModified":"2026-02-11T19:54:32.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/solutions/geotoolkit/tutorials/schematics/real-time-deviated-schematics","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}