Last updated

Header Customization

This tutorial demonstrates how to visually customize track headers. See the Basics Headers tutorial for header fundamentals.

# Containers

Header containers are another type of container and can be used to group headers. Each header container has a header provider. The code below demonstrates how to put the HeaderContainer and a TrackContainer into the same visual, though they can be split into their own separate visuals.

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { LogTrackHeader } from "@int/geotoolkit/welllog/header/LogTrackHeader.ts";
import { UnitFactory } from "@int/geotoolkit/util/UnitFactory.ts";
import { Grid } from "@int/geotoolkit/axis/Grid.ts";
import { AdaptiveTickGenerator } from "@int/geotoolkit/axis/AdaptiveTickGenerator.ts";
import { LogTrack } from "@int/geotoolkit/welllog/LogTrack.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { LogAxis } from "@int/geotoolkit/welllog/LogAxis.ts";
import { LogData } from "@int/geotoolkit/welllog/data/LogData.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { HeaderContainer } from "@int/geotoolkit/welllog/HeaderContainer.ts";
import { LogVisualHeaderProvider } from "@int/geotoolkit/welllog/header/LogVisualHeaderProvider.ts";
import { TrackContainer } from "@int/geotoolkit/welllog/TrackContainer.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { createCurve } from "/src/code/WellLog/Headers/HeaderCustomization/common.ts";
const DEPTH_TRACK_WIDTH = 40;
const MIN_DEPTH = 100;
const MAX_DEPTH = 200;
const createTestData = function(min, max) {
  min = min != null ? min : MIN_DEPTH;
  max = max != null ? max : MAX_DEPTH;
  const depths = [];
  const values = [0, 5, 15, 25, 20, 30, 10, 5, 10, 5, 10, 15, 20, 8, 10, 25, 22, 10, 3, 5];
  const valuesLength = values.length;
  for (let i = 0; i < valuesLength; i++) {
    depths[i] = min + i * (max - min) / (valuesLength - 1);
  }
  return new LogData("CALI").setName("Cali").setValues(depths, values);
};
const createDepthTrack = function(props) {
  const trackWidth = props["trackWidth"] != null ? props["trackWidth"] : DEPTH_TRACK_WIDTH;
  const trackStart = props["trackStart"] != null ? props["trackStart"] : 35;
  const minDepth = props["minDepth"] != null ? props["minDepth"] : MIN_DEPTH;
  const maxDepth = props["maxDepth"] != null ? props["maxDepth"] : MAX_DEPTH;
  const axis = new LogAxis().setName("Depth");
  const bounds = new Rect(trackStart, minDepth, trackStart + trackWidth, maxDepth);
  const track = new LogTrack().setLineStyle(KnownColors.DarkGray).setBounds(bounds).setDepthLimits(minDepth, maxDepth);
  track.addChild(axis);
  return track;
};
const createLinearTrack = function(props) {
  const trackWidth = props["trackWidth"] != null ? props["trackWidth"] : 150;
  const trackStart = props["trackStart"] != null ? props["trackStart"] : 35;
  const minDepth = props["minDepth"] != null ? props["minDepth"] : MIN_DEPTH;
  const maxDepth = props["maxDepth"] != null ? props["maxDepth"] : MAX_DEPTH;
  const vTicks = new AdaptiveTickGenerator();
  vTicks.setMinSpan(6);
  vTicks.setTickStyle("major", KnownColors.Gray);
  vTicks.setTickStyle("minor", KnownColors.LightGray);
  vTicks.setTickStyle("edge", KnownColors.Gray);
  const hTicks = new AdaptiveTickGenerator();
  hTicks.setTickStyle("major", KnownColors.Gray);
  hTicks.setTickStyle("minor", KnownColors.LightGray);
  hTicks.setTickStyle("edge", KnownColors.Gray);
  const grid = new Grid(hTicks, vTicks);
  const bounds = new Rect(trackStart, minDepth, trackStart + trackWidth, maxDepth);
  const track = new LogTrack().setLineStyle(KnownColors.DarkGray).setBounds(bounds).setDepthLimits(minDepth, maxDepth).addChild(grid);
  return track;
};
function createScene(canvas) {
  const parentGroup = new Group();
  const trackContainer = new TrackContainer();
  const headerBounds = new Rect(0, 0, canvas.clientWidth, 40);
  const headerProvider = LogVisualHeaderProvider.getDefaultInstance().clone();
  const headerContainer = new HeaderContainer(headerProvider).setBounds(headerBounds);
  parentGroup.addChild([headerContainer, trackContainer]);
  const data = createTestData(0, 300);
  const curve = createCurve(data);
  const depthTrack = createDepthTrack({
    "trackStart": 0
  });
  const linearTrack = createLinearTrack({
    "trackStart": DEPTH_TRACK_WIDTH
  }).addChild(curve);
  trackContainer.addChild([depthTrack, linearTrack]);
  const trackBounds = new Rect(0, 100, linearTrack.getBounds().getRight(), canvas.clientHeight);
  let width = 0;
  for (let i = 0; i < trackContainer.getChildrenCount(); ++i) {
    const track = trackContainer.getChild(i);
    width += track.getBounds().getWidth();
  }
  trackContainer.setBounds(new Rect(0, 0, width, canvas.clientHeight)).setDepthLimits(0, 300);
  trackContainer.setIndexUnit(UnitFactory.getInstance().getUnit("metre")).setDepthLimits(0, 300).adjustPosition(parentGroup.getModelLimits());
  headerContainer.addChild([
    new LogTrackHeader(depthTrack),
    new LogTrackHeader(linearTrack)
  ]);
  const visualHeader = headerProvider.getHeader(curve);
  if (visualHeader != null) {
    visualHeader.setDisplayString("feet");
  }
  trackContainer.setBounds(trackBounds);
  return new Plot({
    "canvaselement": canvas,
    "root": parentGroup
  });
}
export { createScene };

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

# Customization

To create a custom header from LogVisualHeader, the 'render' method must be overridden.
If the header type is expected to be instantiated from within LogVisualHeaderProvider, then the method 'clone' must be overridden.

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { LogData } from "@int/geotoolkit/welllog/data/LogData.ts";
import { HeaderContainer } from "@int/geotoolkit/welllog/HeaderContainer.ts";
import { LogVisualHeaderProvider } from "@int/geotoolkit/welllog/header/LogVisualHeaderProvider.ts";
import { TrackContainer } from "@int/geotoolkit/welllog/TrackContainer.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { CustomVisualHeader } from "/src/code/WellLog/Headers/HeaderCustomization/customVisualHeader.ts";
import { createCurve } from "/src/code/WellLog/Headers/HeaderCustomization/common.ts";
const MIN_DEPTH = 100;
const MAX_DEPTH = 200;
const createGeochemicalTestData = function() {
  const datasource = new LogData("FEO");
  datasource.setName("FEO");
  const depths = [];
  const values = [7, 5, 15, 25, 20, 30, 10, 5, 10, 5, 10, 15, 20, 8, 10, 25, 22, 10, 3, 5];
  for (let i = 0; i < 20; ++i) {
    depths[i] = MIN_DEPTH + i * (MAX_DEPTH - MIN_DEPTH) / 19;
  }
  datasource.setValues(depths, values);
  return datasource;
};
function createScene(canvas) {
  const data = createGeochemicalTestData();
  const curve = createCurve(data);
  const parentGroup = new Group();
  const trackContainer = new TrackContainer();
  const headerBounds = new Rect(0, 0, canvas.clientWidth, 40);
  const headerProvider = LogVisualHeaderProvider.getDefaultInstance().clone();
  const headerContainer = new HeaderContainer(headerProvider).setBounds(headerBounds);
  parentGroup.addChild([headerContainer, trackContainer]);
  parentGroup.addChild([
    new CustomVisualHeader(curve).setBounds(new Rect(0, 0, 200, 40))
  ]);
  trackContainer.setBounds(null);
  return new Plot({
    "canvaselement": canvas,
    "root": parentGroup
  });
}
export { createScene };

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

# Default Border

By default, the track header border is not visible. When the border is made visible without specifying its linestyle, the track's border line style is used for the header border.

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { LogBaseTrackHeader } from "@int/geotoolkit/welllog/header/LogBaseTrackHeader.ts";
import { initializeWidget } from "/src/code/WellLog/Headers/HeaderCustomization/common.ts";
function createScene(canvas) {
  const props = initializeWidget();
  for (let i = 0; i < props["headerContainer"].getChildrenCount(); ++i) {
    const header = props["headerContainer"].getChild(i);
    if (header instanceof LogBaseTrackHeader) {
      header.setBorderVisibility(true);
    }
  }
  const plot = new Plot({
    "canvaselement": canvas,
    "root": props["widget"]
  });
  props["widget"].fitToHeight().setHeaderHeight("auto");
  return plot;
}
export { createScene };

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

# Custom Border

The header border's line style can be configured to use a user-specified line style instead of the default border line style. The example below shows a dashed border line style.

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { LogBaseTrackHeader } from "@int/geotoolkit/welllog/header/LogBaseTrackHeader.ts";
import { Patterns } from "@int/geotoolkit/attributes/LineStyle.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { initializeWidget } from "/src/code/WellLog/Headers/HeaderCustomization/common.ts";
function createScene(canvas) {
  const props = initializeWidget();
  for (let i = 0; i < props["headerContainer"].getChildrenCount(); ++i) {
    const header = props["headerContainer"].getChild(i);
    if (header instanceof LogBaseTrackHeader) {
      header.setBorderVisibility(true).setBorderLineStyle({
        "color": KnownColors.LightBlue,
        "width": 1,
        "pattern": Patterns.Dash
      });
    }
  }
  const plot = new Plot({
    "canvaselement": canvas,
    "root": props["widget"]
  });
  props["widget"].fitToHeight().setHeaderHeight("auto");
  return plot;
}
export { createScene };

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

# Custom LogLithology Header Height

By default, LogLithologyHeader automatically calculates the header height based on factors such as the number of lithology header items, header type, and header layout.

However, if needed, you can disable this automatic height calculation by setting the 'autodesiredheight' property to false (which is true by default). After disabling it, you have the option to manually set the desired header height using the setDesiredHeight method for the header.

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { BorderMode, LineType, LogLithology } from "@int/geotoolkit/welllog/LogLithology.ts";
import { TrackType } from "@int/geotoolkit/welllog/TrackType.ts";
import { HeaderType as LogAxisHeaderType } from "@int/geotoolkit/welllog/header/LogAxisVisualHeader.ts";
import { HeaderType as WellLogHeaderType } from "@int/geotoolkit/welllog/header/HeaderType.ts";
import { HeaderType as LithologyHeaderType, LogLithologyHeader } from "@int/geotoolkit/welllog/header/LogLithologyHeader.ts";
import { PatternFactory } from "@int/geotoolkit/attributes/PatternFactory.ts";
import { from } from "@int/geotoolkit/selection/from.ts";
import { loadResources } from "/src/helpers/resources.ts";
import { createWellLogWidget } from "/src/code/WellLog/utils/common.ts";
import { AdaptiveLogLithologyHeader } from "@int/geotoolkit/welllog/header/AdaptiveLogLithologyHeader.ts";
import { DiscreteFillDisplayType } from "@int/geotoolkit/welllog/header/AdaptiveDiscreteFillVisualHeader.ts";
loadResources("patterns");
function getLithologyBorderMode(plot) {
  const lithology = from(plot.getRoot()).where((node) => node instanceof LogLithology).selectFirst();
  return lithology != null ? lithology.getBorderMode() : BorderMode.Middle;
}
function setLithologyBorderMode(plot, mode) {
  const lithologies = from(plot.getRoot()).where((node) => node instanceof LogLithology).selectToArray();
  lithologies.forEach((lithology) => {
    lithology.setBorderMode(mode);
  });
}
function createScene(canvas) {
  const widget = createWellLogWidget().setAxisHeaderType(LogAxisHeaderType.Simple).setDepthLimits(100, 400);
  widget.addTrack(TrackType.IndexTrack);
  const patternFactory = PatternFactory.getInstance();
  const lithology = new LogLithology({
    "depths": [128, 200, 280, 320],
    "titles": ["chert", "shale", "lime"],
    "bordermode": BorderMode.Inside,
    "linetypes": [
      LineType.CONTINUE,
      LineType.CONTINUE,
      LineType.DISCONTINUE,
      LineType.CONTINUE
    ],
    "linestyles": [
      { "color": "rgb(0, 142, 0)", "width": 3 },
      { "color": "rgb(102, 102, 102)", "width": 3 },
      { "color": "rgb(204, 102, 0)", "width": 3 }
    ],
    "fillstyles": [{
      "color": "#7cb342",
      "pattern": patternFactory.getPattern("chert"),
      "foreground": "#dcedc8"
    }, {
      "color": "#bdbdbd",
      "pattern": patternFactory.getPattern("shale")
    }, {
      "color": "#fdd835",
      "pattern": patternFactory.getPattern("lime")
    }]
  });
  widget.addTrack(TrackType.LinearTrack).addChild(lithology);
  widget.addTrack(TrackType.IndexTrack);
  const lithologyHeader = lithology.getHeader(WellLogHeaderType.Header);
  if (lithologyHeader instanceof LogLithologyHeader) {
    lithologyHeader.setHeaderType(LithologyHeaderType.BoxesLeft).setDesiredHeight(50);
  } else if (lithologyHeader instanceof AdaptiveLogLithologyHeader) {
    lithologyHeader.setDiscreteDisplayType(DiscreteFillDisplayType.BoxesLeft).setBoxOptions({
      icon: {
        width: 20,
        height: 20
      }
    });
  }
  widget.setHeaderHeight("auto");
  return {
    plot: new Plot({
      "canvaselement": canvas,
      "root": widget
    }),
    lithology
  };
}
export { createScene, getLithologyBorderMode, setLithologyBorderMode };

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