{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["tabs","tab"]},"type":"markdown"},"seo":{"title":"Visuals Editing","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":"visuals-editing","__idx":0},"children":["Visuals Editing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following canvas demonstrates the tool for creating, moving and resizing visuals. This tool works with WellLog Widgets and allows for easy visual interactive manipulation of Log Visuals. The tool works in both live editing and ghost mode, allows styling of handles, and fires all the events required for full control over user actions. The modes work as follows:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Edit Mode"]}," - Allows moving and resizing along the depth axis of the track. The tool will fire events every time the handle has been captured, dragged, or released by the user."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Insert Mode"]}," - Allows inserting new visuals into a track. The tool then will take over the mouse events, and a ghost representing the position of insertion will be dragged along the mouse pointer. The tool will fire events every time a move occurs."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Delete Mode"]}," - Allows deleting existing visuals from a track."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Note that adjacent lithology ranges are bound to each other. I.e. changing some range geometry will affect the adjacent ranges. To unbound ranges use ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Shift"]}," button."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Information about the events, styling the handles, and other properties for the tool can be found in: ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/doc/classes/geotoolkit.welllog.widgets.tools.welllogvisualseditingtool.welllogvisualseditingtool.html"},"children":["geotoolkit.welllog.widgets.tools.WellLogVisualsEditingTool"]}," class documentation"]},{"$$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 { KnownColors } from \"@int/geotoolkit/util/ColorUtil.ts\";\nimport { HeaderType as LogAxisVisualHeaderType } from \"@int/geotoolkit/welllog/header/LogAxisVisualHeader.ts\";\nimport { TrackType as WellLogTrackType } from \"@int/geotoolkit/welllog/TrackType.ts\";\nimport { LogAnnotation } from \"@int/geotoolkit/welllog/LogAnnotation.ts\";\nimport { EditEvents } from \"@int/geotoolkit/controls/tools/EditEvents.ts\";\nimport { Layer } from \"@int/geotoolkit/scene/Layer.ts\";\nimport { Plot } from \"@int/geotoolkit/plot/Plot.ts\";\nimport { Rect } from \"@int/geotoolkit/util/Rect.ts\";\nimport { from } from \"@int/geotoolkit/selection/from.ts\";\nimport { EditMode } from \"@int/geotoolkit/controls/tools/EditMode.ts\";\nimport { CssStyle } from \"@int/geotoolkit/css/CssStyle.ts\";\nimport { LogMarker } from \"@int/geotoolkit/welllog/LogMarker.ts\";\nimport { LabelFillMode, LineType, LogLithology, NameOrientation } from \"@int/geotoolkit/welllog/LogLithology.ts\";\nimport { SquarePainter } from \"@int/geotoolkit/scene/shapes/painters/SquarePainter.ts\";\nimport { AnchorType } from \"@int/geotoolkit/util/AnchorType.ts\";\nimport { Events, Events as SelectionEvents } from \"@int/geotoolkit/controls/tools/Selection.ts\";\nimport { LogAbstractVisual } from \"@int/geotoolkit/welllog/LogAbstractVisual.ts\";\nimport { LithologyAttributeCallback } from \"/src/code/WellLog/utils/editing/LithologyAttributeCallback.ts\";\nimport { VisualsEditor } from \"/src/code/WellLog/utils/editing/VisualsEditor.ts\";\nimport { createWellLogWidget } from \"/src/code/WellLog/utils/common.ts\";\nimport { LogAnnotationEditor } from \"@int/geotoolkit/welllog/widgets/tools/html/LogAnnotationEditor.ts\";\nimport { LogLithologyEditor } from \"@int/geotoolkit/welllog/widgets/tools/html/LogLithologyEditor.ts\";\nconst LITHOLOGY_MIN_DEVICE_HEIGHT = 5;\nconst activeStyle = new CssStyle(`\n    .geotoolkit.scene.shapes.SymbolShape:active {\n        fillstyle: #00ff00;\n    }\n`);\nconst ghostStyles = {\n  \"fillstyle\": \"rgba(255, 0, 0, 0.3)\",\n  \"linestyle\": {\n    \"color\": KnownColors.DarkRed,\n    \"width\": 1,\n    \"pattern\": [7, 3]\n  }\n};\nconst boundingHandleStyles = {\n  \"linestyle\": {\n    \"color\": KnownColors.DarkBlue,\n    \"pattern\": [3, 3],\n    \"pixelsnapmode\": true\n  }\n};\nconst handleStyles = {\n  \"bbox\": {\n    \"painter\": SquarePainter,\n    \"alignment\": AnchorType.Center,\n    \"sizeisindevicespace\": true,\n    \"width\": 7,\n    \"height\": 7,\n    \"fillstyle\": {\n      \"color\": \"white\",\n      \"shadow\": {\n        \"enable\": true,\n        \"color\": \"rgba(0,0,0,0.3)\",\n        \"offsetx\": 1,\n        \"offsety\": 1\n      }\n    },\n    \"linestyle\": {\n      \"color\": \"#7a7a7a\",\n      \"pixelsnapmode\": true\n    }\n  }\n};\nconst depths = [98, 150, 230, 310];\nconst lithologyNames = [\"Default Range \\u21161\", \"Default Range \\u21162\", \"Default Range \\u21163\"];\nconst fillStyles = [\n  KnownColors.Green,\n  KnownColors.Grey,\n  KnownColors.Yellow\n];\nconst lineTypes = [\n  LineType.DISCONTINUE,\n  LineType.DISCONTINUE,\n  LineType.CONTINUE,\n  LineType.DISCONTINUE\n];\nclass VisualsEditing {\n  constructor(setEditButton) {\n    this.setEditButton = setEditButton;\n    this.setEditButton = setEditButton;\n    this._widget = null;\n  }\n  createBasicWellLogWidget() {\n    const widget = createWellLogWidget().setAxisHeaderType(LogAxisVisualHeaderType.Simple).setDepthLimits(50, 400);\n    widget.addTrack(WellLogTrackType.IndexTrack);\n    widget.addTrack(WellLogTrackType.LinearTrack).addChild([\n      new LogMarker().setDepth(200).setLineStyle(KnownColors.Green)\n    ]);\n    widget.addTrack(WellLogTrackType.IndexTrack);\n    widget.addTrack(WellLogTrackType.LinearTrack).addChild([\n      new LogAnnotation({\n        \"bounds\": new Rect(0, 200, 1, 300),\n        \"text\": \"Text1\",\n        \"textstyle\": \"#7cb342\",\n        \"fillstyle\": \"#fdd835\",\n        \"linestyle\": {\n          \"color\": \"gray\",\n          \"pixelsnapmode\": true\n        },\n        \"fixedheight\": false\n      })\n    ]);\n    widget.addTrack(WellLogTrackType.IndexTrack);\n    widget.addTrack(WellLogTrackType.LinearTrack).addChild([\n      new LogLithology({\n        \"depths\": depths,\n        \"fillstyles\": fillStyles,\n        \"linetypes\": lineTypes,\n        \"names\": lithologyNames,\n        \"nameorientation\": NameOrientation.Regular,\n        \"labelfillmode\": LabelFillMode.NONE\n      })\n    ]);\n    widget.addTrack(WellLogTrackType.IndexTrack);\n    return widget;\n  }\n  addWidgetToCanvas(canvas, widget) {\n    this._widget = widget;\n    this._plot = new Plot({\n      \"canvaselement\": canvas,\n      \"root\": widget\n    });\n    this._widget.fitToWidth().fitToHeight();\n    const manipulatorLayer = new Layer({\n      \"id\": \"manipulator.layer\",\n      \"cssstyle\": activeStyle\n    });\n    widget.getTrackManipulatorLayer().addChild(manipulatorLayer);\n    this._wellLogEditor = this.initializeEditingTool(manipulatorLayer, widget);\n    const selectionTool = this._widget.getToolByName(\"pick\");\n    selectionTool.on(SelectionEvents.onSelectionEnd, (evt, sender, eventArgs) => {\n      if ((this.getMode() & EditMode.CreateWithBounds) !== 0)\n        return;\n      const selection = eventArgs.getSelection();\n      if (selection == null || selection.length === 0)\n        return;\n      let selected = null;\n      for (let i = selection.length - 1; i >= 0; i--) {\n        const node = selection[i];\n        if (node instanceof LogAbstractVisual) {\n          selected = node;\n          break;\n        }\n      }\n      this._wellLogEditor.editNode(selected);\n    });\n    this._widget.getTool().insert(0, this._wellLogEditor);\n    this.initializeInlineEditor(widget);\n    this.setMode(EditMode.EditBbox);\n  }\n  initializeEditingTool(manipulatorLayer, widget) {\n    const editorOpts = {\n      \"mindeviceheight\": LITHOLOGY_MIN_DEVICE_HEIGHT,\n      \"attributecallback\": LithologyAttributeCallback,\n      \"handles\": handleStyles,\n      \"bbox\": boundingHandleStyles,\n      \"ghost\": ghostStyles\n    };\n    return new VisualsEditor({\n      \"layer\": manipulatorLayer,\n      \"widget\": widget,\n      \"editor\": editorOpts\n    }).on(EditEvents.CommandApplying, (evt, sender, args) => {\n      const visual = sender.getShape();\n      if (!(visual instanceof LogAnnotation)) {\n        return;\n      }\n      const command = args.getCommand();\n      switch (command.getEventName()) {\n        case EditEvents.Start:\n        case EditEvents.Continue:\n        case EditEvents.Translated:\n        case EditEvents.Resized:\n          const editor = sender.getEditor();\n          const track = visual.getTrack();\n          const deviceTr = editor.getDeviceTransform();\n          const devBounds = deviceTr.transformRect(editor.getBounds());\n          if (!this.checkCollision(visual, track, devBounds)) {\n            command.reject();\n          }\n      }\n    }).on(EditEvents.End, () => {\n      this.setEditButton(true);\n    });\n  }\n  initializeInlineEditor(widget) {\n    widget.getToolByName(\"pick\").on(Events.onDoubleClick, (event, sender, eventArgs) => {\n      const logVisual = sender.getSelection().filter((node) => node instanceof LogAbstractVisual)[0];\n      if (!(logVisual instanceof LogAnnotation) && !(logVisual instanceof LogLithology)) {\n        return;\n      }\n      const plot = eventArgs.getPlot();\n      const plotPoint = eventArgs.getPlotPoint();\n      if (logVisual instanceof LogAnnotation) {\n        LogAnnotationEditor.editVisual(logVisual, plot, plotPoint);\n      }\n      if (logVisual instanceof LogLithology) {\n        LogLithologyEditor.editVisual(logVisual, plot, plotPoint);\n      }\n      eventArgs.stopPropagation();\n    });\n  }\n  execute(command) {\n    this._wellLogEditor.getHistory().push(command);\n  }\n  getEditingTool() {\n    return this._wellLogEditor;\n  }\n  setInsertMode(nodeId, options) {\n    this._wellLogEditor.insertNode(nodeId, options);\n  }\n  getMode() {\n    return this._wellLogEditor.getEditMode();\n  }\n  setMode(value) {\n    this._wellLogEditor.setEditMode(value);\n  }\n  toggleFreeResizeMode() {\n    this._wellLogEditor.setFreeResizeModeEnabled(!this._wellLogEditor.isFreeResizeModeEnabled());\n  }\n  toggleGhostMode() {\n    let mode = this.getMode();\n    mode ^= EditMode.Ghost;\n    this._wellLogEditor.setEditMode(mode);\n  }\n  undo() {\n    this._wellLogEditor.undo();\n  }\n  redo() {\n    this._wellLogEditor.redo();\n  }\n  getUIOptions() {\n    return {\n      canUndo: this._wellLogEditor.canUndo(),\n      canRedo: this._wellLogEditor.canRedo()\n    };\n  }\n  getHistory() {\n    return this._wellLogEditor.getHistory();\n  }\n  getPlot() {\n    return this._plot;\n  }\n  checkCollision(visual, track, deviceBounds) {\n    const intersect = from(track).where(\n      (node) => node instanceof LogAnnotation && node !== visual && node.getSceneTransform().transformRect(node.getRect()).intersects(deviceBounds)\n    ).selectFirst() != null;\n    return !intersect;\n  }\n}\nfunction createScene(canvas, setEditButton, update) {\n  const app = new VisualsEditing(setEditButton || (() => true));\n  const widget = app.createBasicWellLogWidget();\n  app.addWidgetToCanvas(canvas, widget);\n  app.getHistory().on(EditEvents.CommandApplied, update);\n  return app;\n}\nexport { createScene };\n\ncreateScene(document.querySelector('[ref=\"plot\"]'), this?.setEditButton, this.updateUI.bind(this));\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/AdditionalFunctionality/VisualsEditing/visualsEditing?section=introduction&extract=true","width":"100%","height":"700.5px","style":{"border":"none"}},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"visuals-editor","__idx":1},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#/WellLog/AdditionalFunctionality/VisualsEditing/visualsEditing#VisualsEditor"},"children":["#"]}," Visuals Editor"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following code snippet shows how to manage the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Insert Mode"]}," and set proper editing modes."]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[]}]},"headings":[{"value":"Visuals Editing","id":"visuals-editing","depth":1},{"value":"Visuals Editor","id":"visuals-editor","depth":3}],"frontmatter":{"title":"Visuals Editing","seo":{"title":"Visuals Editing"}},"lastModified":"2026-02-11T19:54:32.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/solutions/geotoolkit/tutorials/well-log/additional-functionality/visuals-editing","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}