Track Schematics

This section provides a simple demonstration of how to use schematics components in tandem with tracks and other elements.

# Component Selection

A selection tool is used to provide information on highlighting on an outside table element. In addition, table rows can be clicked to highlight the corresponding schematic element. The width of the schematic can be adjusted through drag-and-drop.

import { ComponentNodeFactoryRegistry } from "@int/geotoolkit/schematics/factory/ComponentNodeFactoryRegistry.ts";
import { WellLogWidget } from "@int/geotoolkit/welllog/widgets/WellLogWidget.ts";
import { WellBoreData } from "@int/geotoolkit/schematics/data/WellBoreData.ts";
import { LogAbstractVisual } from "@int/geotoolkit/welllog/LogAbstractVisual.ts";
import { ViewMode, WellBoreNode } from "@int/geotoolkit/schematics/scene/WellBoreNode.ts";
import { TrackType } from "@int/geotoolkit/welllog/TrackType.ts";
import { HeaderType } from "@int/geotoolkit/welllog/header/LogAxisVisualHeader.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { LogData } from "@int/geotoolkit/welllog/data/LogData.ts";
import { LogCurve } from "@int/geotoolkit/welllog/LogCurve.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.ts";
import { CssLayout } from "@int/geotoolkit/layout/CssLayout.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Layer } from "@int/geotoolkit/scene/Layer.ts";
import { Events as SelectionEvents } from "@int/geotoolkit/controls/tools/Selection.ts";
import { ComponentNode } from "@int/geotoolkit/schematics/scene/ComponentNode.ts";
import { DataTable } from "@int/geotoolkit/data/DataTable.ts";
import { DataTableAdapter } from "@int/geotoolkit/widgets/data/DataTableAdapter.ts";
import { TableView } from "@int/geotoolkit/widgets/TableView.ts";
import { Dimension } from "@int/geotoolkit/util/Dimension.ts";
import { LineStyle } from "@int/geotoolkit/attributes/LineStyle.ts";
import { FillStyle } from "@int/geotoolkit/attributes/FillStyle.ts";
import { ComponentUtils } from "@int/geotoolkit/schematics/utils/ComponentUtils.ts";
import { NodeTubingBW } from "/src/code/Schematics/TrackSchematics/nodes/nodetubing.ts";
import { NodePackerBW } from "/src/code/Schematics/TrackSchematics/nodes/nodepacker.ts";
import { NodeCementBW } from "/src/code/Schematics/TrackSchematics/nodes/nodecement.ts";
import { NodePerforationBW } from "/src/code/Schematics/TrackSchematics/nodes/nodeperforation.ts";
import { NodeCasingBW } from "/src/code/Schematics/TrackSchematics/nodes/nodecasing.ts";
import { componentsData } from "/src/code/Schematics/TrackSchematics/data/components.ts";
import curveData from "/src/helpers/curveData.json?import";
let leftRectangle, rightRectangle;
const registerSchematicComponents = (data) => {
  const components = componentsData.map((component) => {
    const { name, from, to, outer, inner } = component;
    data.addComponent(name, {
      geometry: {
        depth: { from, to },
        diameter: { outer, inner }
      }
    });
    return JSON.parse(JSON.stringify(data.getProperties()["elements"].slice(-1)[0]));
  });
  return components;
};
const createSchematicTable = (components) => {
  const rows = [];
  components.forEach((component) => {
    const geom = component["data"]["geometry"];
    rows.push([
      component.name,
      ComponentUtils.getDepthFrom(geom),
      ComponentUtils.getDepthTo(geom),
      ComponentUtils.getOuterDiameter(geom),
      ComponentUtils.getInnerDiameter(geom) || 0
    ]);
  });
  const dataTable = new DataTable({
    cols: [
      { name: "Type", type: "string" },
      { name: "Depth (from)", type: "number" },
      { name: "Depth (to)", type: "number" },
      { name: "Outer Radius", type: "number" },
      { name: "Inner Radius", type: "number" }
    ],
    rowsdata: rows
  });
  const bridge = new DataTableAdapter({
    "datatable": dataTable
  });
  return new TableView({
    "horizontalscroll": "floating",
    "verticalscroll": "floating",
    "cols": 4,
    "defaultcellsize": new Dimension(80, 30),
    "highlightcolumnfillstyle": FillStyle.Empty
  }).setData(bridge);
};
const createSchematicRegistry = function() {
  return new ComponentNodeFactoryRegistry(false).setFactory("tubing", (data) => new NodeTubingBW(data)).setFactory("packer", (data) => new NodePackerBW(data)).setFactory("cement", (data) => new NodeCementBW(data)).setFactory("perforation", (data) => new NodePerforationBW(data)).setFactory("casing", (data) => new NodeCasingBW(data));
};
const getGeometryProperty = (geometry, propertyName) => {
  if (Array.isArray(geometry)) {
    geometry = geometry[0];
  }
  return geometry[propertyName];
};
const highlightComponentNode = (componentNode, wellBoreNode, highlightLayer) => {
  if (componentNode === void 0) {
    return;
  }
  let bounds = componentNode.getBounds();
  const diameters = getGeometryProperty(componentNode.getData()["geometry"], "diameter");
  if (diameters["inner"] === void 0) {
    bounds = wellBoreNode.getSceneTransform().transform(bounds);
    bounds = highlightLayer.getSceneTransform().inverseTransform(bounds);
    leftRectangle.setBounds(bounds);
    leftRectangle.setVisible(true);
  } else {
    const width = (diameters["outer"] - diameters["inner"]) / 2;
    let leftBounds = new Rect(bounds.getLeft(), bounds.getTop(), bounds.getLeft() + width, bounds.getTop() + bounds.getHeight());
    let rightBounds = new Rect(bounds.getRight() - width, bounds.getTop(), bounds.getRight(), bounds.getTop() + bounds.getHeight());
    leftBounds = wellBoreNode.getSceneTransform().transform(leftBounds);
    leftBounds = highlightLayer.getSceneTransform().inverseTransform(leftBounds);
    rightBounds = wellBoreNode.getSceneTransform().transform(rightBounds);
    rightBounds = highlightLayer.getSceneTransform().inverseTransform(rightBounds);
    leftRectangle.setBounds(leftBounds);
    rightRectangle.setBounds(rightBounds);
    leftRectangle.setVisible(true);
    rightRectangle.setVisible(true);
  }
};
const createWellLogWidget = function(wellBoreNode) {
  const widget = new WellLogWidget({
    "header": { "visible": true },
    "footer": { "visible": false },
    "border": { "visible": true },
    "horizontalscrollable": false,
    "verticalscrollable": false,
    "nodefilter": (node) => node instanceof LogAbstractVisual || node instanceof WellBoreNode,
    "tools": {
      "cursortracking": {
        "tooltip": {
          "enabled": true
        }
      }
    }
  }).setAxisHeaderType(HeaderType.Simple).setHeaderHeight(50).setIndexUnit("m").setDepthLimits(0, 500);
  widget.addTrack(TrackType.IndexTrack);
  widget.addTrack(TrackType.LinearTrack).setWidth(175).addChild([wellBoreNode]);
  const values = curveData["CALI"].slice(0, 500);
  const depths = [];
  for (let i = 1; i <= 500; ++i) {
    depths.push(i);
  }
  widget.addTrack(TrackType.LinearTrack).setWidth(175).addChild(
    new LogCurve(
      new LogData({
        "name": "CALI",
        "depths": depths,
        "values": values
      })
    ).setLineStyle("orange")
  );
  return widget;
};
const createScene = (canvas) => {
  const wellBoreData = new WellBoreData();
  const components = registerSchematicComponents(wellBoreData);
  const geometryBounds = wellBoreData.getGeometryBounds();
  const wellBoreNode = new WellBoreNode({
    "data": wellBoreData,
    "viewmode": ViewMode.Regular,
    "registry": createSchematicRegistry()
  }).setBounds(new Rect(0.2, geometryBounds.getTop(), 0.8, geometryBounds.getBottom()));
  const wellLogWidget = createWellLogWidget(wellBoreNode);
  const schematicTable = createSchematicTable(components);
  const plot = new Plot({
    "canvaselement": canvas,
    "autosize": false,
    "root": new Group().setAutoModelLimitsMode(true).setLayout(new CssLayout()).addChild([
      wellLogWidget.setLayoutStyle({
        "top": 0,
        "left": 0,
        "bottom": 0,
        "width": 400
      }),
      schematicTable.setLayoutStyle({
        "top": 0,
        "right": 0,
        "bottom": 0,
        "width": 400
      })
    ])
  });
  wellLogWidget.fitToHeight();
  const highlight = new Layer();
  const highlightLineStyle = new LineStyle("rgba(128,129,128, 0.5)");
  const highlightFillStyle = new FillStyle("rgba(189,89,89,0.5)");
  leftRectangle = new Rectangle({
    "linestyle": highlightLineStyle,
    "fillstyle": highlightFillStyle,
    "visible": false
  });
  rightRectangle = new Rectangle({
    "linestyle": highlightLineStyle,
    "fillstyle": highlightFillStyle,
    "visible": false
  });
  highlight.addChild([leftRectangle, rightRectangle]);
  wellLogWidget.getTrackContainer().addLayer(highlight);
  const selectionOff = () => {
    schematicTable.setActiveRow(null);
    leftRectangle.setVisible(false);
    rightRectangle.setVisible(false);
  };
  schematicTable.getToolByName("TableViewSelection").on(SelectionEvents.onPick, () => {
    leftRectangle.setVisible(false);
    rightRectangle.setVisible(false);
    const index = schematicTable.getActiveRows()[0];
    const componentNode = wellBoreNode.getComponents().toArray()[index];
    highlightComponentNode(componentNode, wellBoreNode, highlight);
  });
  wellLogWidget.getToolByName("pick").setNodeFilter((nodes) => nodes.filter((node) => node instanceof ComponentNode)).on(SelectionEvents.onPick, (evt, sender, eventArgs) => {
    selectionOff();
    const nodes = eventArgs.getSelection();
    if (nodes && nodes.length > 0) {
      const componentNode = nodes[nodes.length - 1];
      highlightComponentNode(componentNode, wellBoreNode, highlight);
      const id = componentNode.getId();
      let index = -1;
      for (let i = 0; i < components.length; ++i) {
        if (components[i].data.id === id) {
          index = i;
          break;
        }
      }
      schematicTable.setActiveRow(index);
    }
  });
  return plot;
};
export { createScene };

createScene(document.querySelector('[ref="plot"]'));

# Schematics Components

The tutorial demonstrates how to incorporate a WellBoreNode into WellLogJS's track container. Please refer to the WellLogJS Introduction tutorial and the Tracks tutorial for more details on WellLogs, Tracks and Track Containers.
In this tutorial, depth/index tracks are paired with linear tracks so the schematic element can be analyzed easily. The WellBoreNode shape is created using the WellBoreData, then it is added to a linear track as a child. Log Curves are also added to the tracks for a realistic visual comparison.
Also, this tutorial shows how to incorporate dragging and dropping schematics components to the track and how to highlight schematics elements.

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { WellLogWidget } from "@int/geotoolkit/welllog/widgets/WellLogWidget.ts";
import { AnnotatedWidget } from "@int/geotoolkit/widgets/AnnotatedWidget.ts";
import { ComponentNodeFactoryRegistry } from "@int/geotoolkit/schematics/factory/ComponentNodeFactoryRegistry.ts";
import { SVGComponentsLoader } from "@int/geotoolkit/schematics/factory/SVGComponentsLoader.ts";
import { Selector } from "@int/geotoolkit/selection/Selector.ts";
import { LogData } from "@int/geotoolkit/welllog/data/LogData.ts";
import { WellBoreData } from "@int/geotoolkit/schematics/data/WellBoreData.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { RestrictVisibleModelLimitsStrategy } from "@int/geotoolkit/scene/RestrictVisibleModelLimitsStrategy.ts";
import { WellBoreNode } from "@int/geotoolkit/schematics/scene/WellBoreNode.ts";
import { DefaultLabelingStrategy } from "@int/geotoolkit/schematics/labeling/DefaultLabelingStrategy.ts";
import { LocationType } from "@int/geotoolkit/schematics/labeling/LocationType.ts";
import { WellBoreWithLabels } from "@int/geotoolkit/schematics/scene/WellBoreWithLabels.ts";
import { ComponentNode } from "@int/geotoolkit/schematics/scene/ComponentNode.ts";
import { TrackType } from "@int/geotoolkit/welllog/TrackType.ts";
import { LogCurve } from "@int/geotoolkit/welllog/LogCurve.ts";
import { SymbolShape } from "@int/geotoolkit/scene/shapes/SymbolShape.ts";
import { AnchorType } from "@int/geotoolkit/util/AnchorType.ts";
import { Node } from "@int/geotoolkit/scene/Node.ts";
import { CirclePainter } from "@int/geotoolkit/scene/shapes/painters/CirclePainter.ts";
import { DragAndDrop, Events as DragEvents } from "@int/geotoolkit/controls/tools/DragAndDrop.ts";
import { ComponentNodeProxy } from "@int/geotoolkit/schematics/scene/ComponentNodeProxy.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { SymbolLabelShape } from "@int/geotoolkit/schematics/labeling/SymbolLabelShape.ts";
import { SquarePainter } from "@int/geotoolkit/scene/shapes/painters/SquarePainter.ts";
import { HorizontalBoxLayout } from "@int/geotoolkit/layout/HorizontalBoxLayout.ts";
import { FitScaleScrollStrategy, Mode } from "@int/geotoolkit/scene/FitScaleScrollStrategy.ts";
import { Events as DomEvents } from "@int/geotoolkit/dom.ts";
import { Layer } from "@int/geotoolkit/scene/Layer.ts";
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.ts";
import { Events as SelectionEvents, Selection } from "@int/geotoolkit/controls/tools/Selection.ts";
import { PointerMode } from "@int/geotoolkit/controls/tools/PointerMode.ts";
import curveData from "/src/helpers/curveData.json?import";
import { createWellBoreData } from "/src/code/Schematics/TrackSchematics/data/wellboredata.ts";
import { arWellCompletion } from "/src/code/Schematics/TrackSchematics/data/arWellCompletion.ts";
import { SchematicsComponentToolTip } from "/src/code/Schematics/TrackSchematics/schematisToolTip.ts";
const minDepth = 0;
let maxDepth = 14589;
const createLogData = (logName) => {
  const data = curveData[logName];
  const dDepth = (maxDepth - minDepth) / (data.length - 1);
  const depthData = [];
  for (let iValue = 0; iValue < data.length; ++iValue) {
    depthData.push(minDepth + iValue * dDepth);
  }
  return new LogData(logName).setValues(depthData, data);
};
const addWellBoreData = (componentName) => {
  const wellBoreData = new WellBoreData();
  const from = 0;
  const getDescription = (name) => " " + name.replace(/ /g, " <br/> ") + " ";
  const curData = {
    "description": getDescription(componentName),
    "geometry": { "depth": { "from": from, "to": from + 85 }, "diameter": { "outer": 200, "inner": 160 } }
  };
  if (componentName === "drilling fluid") {
    const range = curData["geometry"];
    range["level"] = {
      "from": from + 25,
      "to": from + 85
    };
  }
  wellBoreData.addComponent(componentName, curData);
  return wellBoreData;
};
const initializeCompontensList = (componentNames, registry) => {
  const componentHeight = 100;
  const heightBounds = 85;
  const wellBoreWidth = 70;
  const labels = [];
  for (let iCanvas = 0; iCanvas < componentNames.length; iCanvas++) {
    const item = componentNames[iCanvas];
    const wellBoreData = addWellBoreData(item);
    const wellBoreNode = new WellBoreNode({
      "data": wellBoreData,
      "registry": registry
    });
    const labelingStrategy = new DefaultLabelingStrategy({
      "defaultlocation": LocationType.Right,
      "connectorshape": null
    });
    const label = new WellBoreWithLabels({
      "wellborenode": wellBoreNode,
      "labeling": {
        "strategy": labelingStrategy,
        "labelshape": new SymbolLabelShape({
          "painter": SquarePainter,
          "textstyle": {
            "font": "12px Roboto"
          }
        }),
        "connectorshape": null
      }
    });
    const ml = label.getModelLimits();
    const boundsWB = new Rect(ml.getLeft(), ml.getTop(), ml.getLeft() + wellBoreWidth, ml.getBottom());
    wellBoreNode.setBounds(boundsWB);
    const position = componentHeight * iCanvas;
    label.setBounds(new Rect(0, position, 200, position + heightBounds));
    labels.push(label);
  }
  const dragAndDrop = initializeDragAndDrop();
  return new AnnotatedWidget({
    "border": null,
    "tools": {
      "crosshair": {
        "enabled": false
      },
      "verticalscroll": {
        "size": 10,
        "options": {
          "rounded": true
        }
      }
    },
    "model": new Group().setModelLimits(new Rect(0, 0, 160, componentNames.length * componentHeight)).addChild(labels),
    "annotationssizes": {
      "north": 0,
      "south": 0,
      "west": 0,
      "east": 15
    }
  }).setScaleScrollStrategy(new RestrictVisibleModelLimitsStrategy()).scaleModel(1, 16).connectTool(dragAndDrop);
};
const createSchematicsHighlightingLayer = (widget, wellBoreNode) => {
  const layer = new Layer();
  const selectedRectangle = new Rectangle({
    "linestyle": {
      "color": "rgba(128,129,128, 0.5)"
    },
    "fillstyle": {
      "color": "rgba(189,89,89,0.5)"
    },
    "visible": false
  });
  layer.addChild(selectedRectangle);
  widget.getTrackContainer().addLayer(layer);
  const onPick = (evt, sender, event) => {
    const nodes = event.getSelection();
    if (nodes == null || nodes.length < 1) {
      return;
    }
    const node = nodes[nodes.length - 1];
    if (node) {
      let rect = node.getBounds();
      rect = wellBoreNode.getSceneTransform().transform(rect);
      rect = layer.getSceneTransform().inverseTransform(rect);
      selectedRectangle.setBounds(rect);
      selectedRectangle.setVisible(true);
    }
  };
  const onSelectionEnd = (evt, sender) => {
    const nodes = sender.getSelection();
    if (nodes == null || nodes.length === 0) {
      selectedRectangle.setVisible(false);
    }
  };
  const selectionTool = new Selection(layer).setPointerMode(PointerMode.Hover).setNodeFilter((nodes) => nodes.filter((node) => node instanceof ComponentNode)).on(SelectionEvents.onPick, onPick).on(SelectionEvents.onSelectionEnd, onSelectionEnd);
  widget.getTool().add(selectionTool);
  return layer;
};
const createWellLogWidget = function(wellBoreNode) {
  const logGR = createLogData("GR");
  const logCALI = createLogData("CALI");
  const wellLogWidget = new WellLogWidget({
    "header": { "visible": false },
    "footer": { "visible": false },
    "border": { "visible": true },
    "verticalscrollable": false,
    "horizontalscrollable": false
  }).setScaleScrollStrategy(new FitScaleScrollStrategy({
    "mode": Mode.FitVertical
  })).setDepthLimits(minDepth, maxDepth);
  wellLogWidget.addTrack(TrackType.IndexTrack).setWidth(50);
  wellLogWidget.addTrack(TrackType.LinearTrack).setWidth(100).addChild([
    new LogCurve(logGR).setLineStyle({
      "color": KnownColors.Orange
    })
  ]);
  wellLogWidget.addTrack(TrackType.LinearTrack).setWidth(250).addChild([wellBoreNode, new LogCurve(logCALI).setLineStyle("blue").setMicroPosition(0.05, 0.45).setNormalizationLimits(logCALI.getMaxValue(), logCALI.getMinValue()), new LogCurve(logCALI).setLineStyle("green").setMicroPosition(0.55, 0.95)]);
  wellLogWidget.addTrack(TrackType.IndexTrack).setWidth(50);
  wellLogWidget.getToolByName("splitter").setEnabled(false);
  wellLogWidget.getToolByName("tooltip").setEnabled(true);
  createSchematicsHighlightingLayer(wellLogWidget, wellBoreNode);
  return wellLogWidget;
};
const initializeCustomToolTip = function(wellLogWidget, wellBoreNode) {
  let handle;
  wellLogWidget.getTrackManipulatorLayer().addChild([
    handle = new SymbolShape({
      "width": 8,
      "height": 8,
      "alignment": AnchorType.Center,
      "sizeisindevicespace": true,
      "painter": CirclePainter,
      "linestyle": "#1565C0",
      "fillstyle": "#C1E3E8",
      "visible": false
    })
  ]);
  wellLogWidget.getToolByName("tooltip").getRegistry().register(ComponentNode.getClassName(), new SchematicsComponentToolTip());
  const updateHandle = (plot, plotPoint) => {
    const nodes = Selector.select(plot.getRoot(), plotPoint, 2).filter((node) => node instanceof ComponentNode || node instanceof LogCurve);
    if (nodes.length === 0) {
      handle.setVisible(false);
      return;
    }
    const selection = nodes[0];
    const wellLogWidget2 = Node.findParent(selection, WellLogWidget);
    const manipulatorPoint = wellLogWidget2.getTrackManipulatorLayer().getSceneTransform().inverseTransformPoint(plotPoint);
    const color = selection instanceof LogCurve ? selection.getLineStyle().getColor() : "#C1E3E8";
    handle.setAnchor(manipulatorPoint).setFillStyle(color, true).setVisible(true);
  };
  wellLogWidget.on(DomEvents.Hover, (event, sender, eventArgs) => updateHandle(eventArgs.getPlot(), eventArgs.getPlotPoint()));
  return wellLogWidget;
};
const initializeDragAndDrop = function() {
  const dragFilter = (nodes) => {
    for (let j = nodes.length - 1; j >= 0; j--) {
      if (nodes[j] instanceof ComponentNodeProxy) {
        return nodes[j];
      }
    }
    return null;
  };
  const onDrop = (evt, source, args) => {
    const plotPoint = args.getPlotPoint();
    const componentName = args.getElements()[0].getName();
    const nodes = Selector.select(args.getPlot().getRoot(), plotPoint.x, plotPoint.y, 2);
    let wellLogWidget;
    let wellBoreNode;
    let isTrackSchematics = false;
    for (let i = nodes.length - 1; i >= 0; i--) {
      const node = nodes[i];
      if (node instanceof WellLogWidget) {
        wellLogWidget = node;
      }
      if (node instanceof WellBoreNode) {
        wellBoreNode = node;
        isTrackSchematics = true;
      }
    }
    if (wellLogWidget == null || !isTrackSchematics) {
      return;
    }
    const modelPos = wellBoreNode.getSceneTransform().inverseTransform(plotPoint);
    const depthModel = modelPos.getY();
    const from = depthModel;
    let length = 500;
    const sLength = Number(prompt("Please enter length of component", length.toString()));
    if (sLength != null && sLength > 0) {
      length = sLength;
      wellBoreNode.getWellBoreData().addComponent(componentName, {
        geometry: {
          depth: { from, to: from + length },
          diameter: { outer: 2.5 }
        }
      });
      maxDepth += length;
      wellBoreNode.setBounds(new Rect(0.25, minDepth, 0.75, maxDepth));
      wellLogWidget.setDepthLimits(minDepth, maxDepth);
    }
    if (sLength != null && sLength <= 0) {
      alert("Invalid length of component");
    }
  };
  const dragAndDrop = new DragAndDrop({
    "enabled": true,
    "nodefilters": [{
      "drag": dragFilter
    }]
  }).on(DragEvents.onDrop, onDrop);
  return dragAndDrop;
};
const createScene = async (canvas) => {
  const registry = new ComponentNodeFactoryRegistry();
  const loader = new SVGComponentsLoader({
    "registry": registry,
    "path": RESOURCES + "svg/components.json"
  });
  const registryJS = new ComponentNodeFactoryRegistry();
  await loader.load();
  registry.setFactory("perforation", registryJS.getFactory("perforation"));
  registry.setFactory("cement", registryJS.getFactory("cement"));
  const compontensList = initializeCompontensList(arWellCompletion, registry);
  const wellBoreData = createWellBoreData();
  const wellBoreNode = new WellBoreNode({
    "data": wellBoreData,
    "registry": registry
  }).setBounds(new Rect(0.25, minDepth, 0.75, maxDepth));
  const wellLogWidget = createWellLogWidget(wellBoreNode);
  initializeCustomToolTip(wellLogWidget, wellBoreNode);
  const plot = new Plot({
    "canvaselement": canvas,
    "root": new Group().setLayout(new HorizontalBoxLayout()).addChild([
      compontensList.setLayoutStyle({
        "width": "200px"
      }).setMarginsStyle({ right: "2px" }),
      wellLogWidget.setMarginsStyle({ left: "2px" })
    ])
  });
  wellLogWidget.fitToHeight().fitToWidth();
  return plot;
};
export { createScene };

createScene(document.querySelector('[ref="plot"]'));

# Adding the WellBore shape to WellLog widget

This code sample shows how to add the WellBore shape to the WellLog Widget.

# Drag and Drop Functionality

This example shows how to create a Schematic Builder to edit a bottom hole assembly. The drag and drop functionality is implemented through the use of mouse event listeners. Use the mouse to drag and drop the schematic components in the following canvas.

import { ComponentNodeFactoryRegistry } from "@int/geotoolkit/schematics/factory/ComponentNodeFactoryRegistry.ts";
import { SVGComponentsLoader } from "@int/geotoolkit/schematics/factory/SVGComponentsLoader.ts";
import { WellBoreData } from "@int/geotoolkit/schematics/data/WellBoreData.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { WellBoreNode } from "@int/geotoolkit/schematics/scene/WellBoreNode.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { DragAndDrop, Events as DragEvents } from "@int/geotoolkit/controls/tools/DragAndDrop.ts";
import { ComponentNodeProxy } from "@int/geotoolkit/schematics/scene/ComponentNodeProxy.ts";
import { CssLayout } from "@int/geotoolkit/layout/CssLayout.ts";
import { Tools } from "@int/geotoolkit/controls/tools/Tools.ts";
import { init } from "@int/geotoolkit/base.js";
import { ComponentUtils } from "@int/geotoolkit/schematics/utils/ComponentUtils.ts";
import { dragndropArray } from "/src/code/Schematics/TrackSchematics/data/dragndrop.ts";
init({ "imports": [Tools] });
const updateComponents = (components) => {
  const data = new WellBoreData();
  let start = 0;
  for (let i = 0; i < components.length; ++i) {
    const length = components[i]["length"];
    data.addComponent(
      components[i]["name"],
      {
        "description": components[i]["description"],
        "geometry": {
          "depth": { "from": start, "to": start + length },
          "diameter": {
            "outer": components[i]["diameter"]["outer"],
            "inner": components[i]["diameter"]["inner"]
          }
        }
      }
    );
    start += length;
  }
  return data;
};
const createScene = async (canvas) => {
  const registry = new ComponentNodeFactoryRegistry();
  const loader = new SVGComponentsLoader({
    "registry": registry,
    "path": RESOURCES + "svg/components.json"
  });
  await loader.load();
  const componentArray = dragndropArray;
  const data = new WellBoreData();
  let start = 0;
  for (let i = 0; i < componentArray.length; i++) {
    data.addComponent(
      componentArray[i]["name"],
      {
        "description": componentArray[i]["description"],
        "geometry": {
          "depth": {
            "from": start,
            "to": start + componentArray[i]["length"]
          },
          "diameter": {
            "outer": componentArray[i]["diameter"]["outer"],
            "inner": componentArray[i]["diameter"]["inner"]
          }
        }
      }
    );
    start += componentArray[i]["length"];
  }
  let targetComponent = null;
  const placeholder = {
    "name": "empty",
    "description": "empty",
    "length": 25,
    "diameter": {
      "outer": 0.5,
      "inner": 0
    }
  };
  let wellBoreNode;
  const rootGroup = new Group().setAutoModelLimitsMode(true).setLayout(new CssLayout()).addChild([
    wellBoreNode = new WellBoreNode({
      "data": data,
      "registry": registry
    }).setLayoutStyle({
      "left": 65,
      "top": 0,
      "width": 35,
      "bottom": 0
    })
  ]);
  const plot = new Plot({
    "canvaselement": canvas,
    "root": rootGroup
  });
  const onDrag = (nodes) => {
    let component;
    for (let j = 0; j < nodes.length; ++j) {
      const node = nodes[j];
      if (node instanceof ComponentNodeProxy) {
        component = node;
      }
    }
    if (!component)
      return null;
    const entity = component.getGeometryData();
    const componentFrom = ComponentUtils.getDepthFrom(entity);
    const componentTo = ComponentUtils.getDepthTo(entity);
    const nodeFilter = (element) => ComponentUtils.getDepthFrom(element["data"]["geometry"]) === componentFrom && ComponentUtils.getDepthTo(element["data"]["geometry"]) === componentTo;
    for (let k = nodes.length - 1; k >= 0; k--) {
      const node = nodes[k];
      if (node instanceof WellBoreNode) {
        const components = node.getWellBoreData().getProperties()["elements"];
        const index = components.findIndex(nodeFilter);
        if (index >= 0) {
          targetComponent = componentArray[index];
          componentArray.splice(index, 1, placeholder);
        }
        break;
      }
    }
    return component;
  };
  const onDropTool = () => {
    if (targetComponent == null)
      return;
    const index = componentArray.findIndex((element) => element["name"] === "empty");
    if (index < 0)
      return;
    componentArray.splice(index, 1, targetComponent);
    wellBoreNode.setWellBoreData(updateComponents(componentArray));
    targetComponent = null;
  };
  const onDragTool = (event, source, args) => {
    const pt = args.getPlotPoint();
    const components = data.getProperties()["elements"];
    if (targetComponent !== null) {
      const index = components.findIndex((element) => ComponentUtils.getDepthFrom(element["data"]["geometry"]) >= pt.getY());
      if (index >= 0) {
        const emptyIndex = componentArray.findIndex((element) => element["name"] === "empty");
        if (emptyIndex >= 0)
          componentArray.splice(emptyIndex, 1);
        componentArray.splice(index, 0, placeholder);
        wellBoreNode.setWellBoreData(updateComponents(componentArray));
      }
    }
  };
  const dragAndDrop = new DragAndDrop({
    "nodefilters": [{
      "drag": onDrag
    }]
  }).setEnabled(true).on(DragEvents.onDrop, onDropTool).on(DragEvents.onDrag, onDragTool);
  plot.getTool().add(dragAndDrop);
  return plot;
};
export { createScene };

createScene(document.querySelector('[ref="plot"]'));

# Drag and Drop configure