Log2D

This WellLog – Log2D tutorial describes how to add the Log2D visual to the track. It represents data between 0–360 degrees/2PI along a 2D plane. Data along a horizontal cross section at a unique depth is represented by a 2D row. The depth and a list of 'angles' with corresponding 'values' is used to generate each Log2DDataRow. Log2DDataRow is then added to Log2DVisualData, which is used by the Log2DVisual to generate the graphic display. The color provider is called for each unique value to provide the color.

Log2D has different display modes: the discrete mode, where the color is assigned to the color of that interval and the interpolated mode where the color is interpolated depending on the exact value in the color interval.

Log2D can be used to display FMI logs (Acoustic/Optic borehole imaging) or density logs.

# Log2D Data

Log2D supports two types of data sources: ArrayLogDataSource and Log2DVisualData. ArrayLogDataSource is an adapter for DataTable or DataTableView and allows sharing the same data.

Angle Limits

0

360

0

360

Offset

0

Alignment Type

Right

Flip Colors

Wrap Interpolation

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { ScalingOptions } from "@int/geotoolkit/scene/exports/ScalingOptions.ts";
import { FontSubType } from "@int/geotoolkit/pdf/FontSubType.ts";
import { HeaderComponent } from "@int/geotoolkit/scene/exports/HeaderComponent.ts";
import { ImageCompression } from "@int/geotoolkit/pdf/ImageCompression.ts";
import { SpeedCompression } from "@int/geotoolkit/pdf/SpeedCompression.ts";
import { DefaultColorProvider } from "@int/geotoolkit/util/DefaultColorProvider.ts";
import { from } from "@int/geotoolkit/selection/from.ts";
import { ColumnAlignment, Log2DVisual, PlotTypes } from "@int/geotoolkit/welllog/Log2DVisual.ts";
import { TrackType } from "@int/geotoolkit/welllog/TrackType.ts";
import { HeaderType } from "@int/geotoolkit/welllog/header/LogAxisVisualHeader.ts";
import { Orientation } from "@int/geotoolkit/util/Orientation.ts";
import { Range } from "@int/geotoolkit/util/Range.ts";
import { CompositeLog2DVisualHeader } from "@int/geotoolkit/welllog/header/CompositeLog2DVisualHeader.ts";
import { ArrayLogDataSource } from "@int/geotoolkit/welllog/data/ArrayLogDataSource.ts";
import { DataTable } from "@int/geotoolkit/data/DataTable.ts";
import { HttpClient } from "@int/geotoolkit/http/HttpClient.ts";
import { create2DVisual } from "/src/code/WellLog/utils/util.ts";
import font from "/src/assets/fonts/roboto.ttf?import";
import { createWellLogWidget } from "/src/code/WellLog/utils/common.ts";
const httpClient = HttpClient.getInstance().getHttp();
const alignmentTypes = ["Left", "Center", "Table", "Right"];
let base64Font = "";
function generateData() {
  const minDepth = 100, maxDepth = 500;
  const depthStep = (maxDepth - minDepth) / 10;
  const table = new DataTable({
    "cols": [{
      "name": "depth",
      "type": "number"
    }, {
      "name": "val1",
      "type": "number"
    }, {
      "name": "val2",
      "type": "number"
    }, {
      "name": "val3",
      "type": "number"
    }, {
      "name": "val4",
      "type": "number"
    }, {
      "name": "val5",
      "type": "number"
    }]
  });
  for (let i = 1; i < 10; i++) {
    const depth = minDepth + depthStep * i;
    table.addRow([depth, -2, -1, 0, 1, -2]);
  }
  return new ArrayLogDataSource({
    "datatable": table,
    "angles": {
      "values": [0, Math.PI / 2, Math.PI, Math.PI * 3 / 2, Math.PI * 2]
    }
  });
}
function createScene(canvas) {
  const data = generateData();
  const widget = createWellLogWidget({
    "range": new Range(data.getMinDepth(), data.getMaxDepth())
  }).setOrientation(Orientation.Vertical).setAxisHeaderType(HeaderType.Simple).scale(2);
  const headerProvider = widget.getHeaderContainer().getHeaderProvider();
  headerProvider.registerHeaderProvider(Log2DVisual.getClassName(), new CompositeLog2DVisualHeader());
  widget.addTrack(TrackType.IndexTrack);
  widget.addTrack(TrackType.LinearTrack).addChild([
    create2DVisual(data, "Dataset #1", 0, "#fff9c4", true).setPlotType(PlotTypes.Step)
  ]);
  widget.addTrack(TrackType.IndexTrack);
  widget.addTrack(TrackType.LinearTrack).addChild([
    create2DVisual(data, "Dataset #2", 0, "#fff9c4").setPlotType(PlotTypes.Linear)
  ]);
  widget.addTrack(TrackType.IndexTrack);
  return new Plot({
    "canvaselement": canvas,
    "root": widget
  });
}
function setOffset(plot, value) {
  const widget = plot.getRoot();
  from(widget).where('node => class(node) == "geotoolkit.welllog.Log2DVisual"').select((log2d) => {
    log2d.setOffsets(value * Math.PI / 180);
  });
}
function setAngle(plot, value) {
  const minAngle = value[0] * Math.PI / 180;
  const maxAngle = value[1] * Math.PI / 180;
  const widget = plot.getRoot();
  from(widget).where('node => class(node) == "geotoolkit.welllog.Log2DVisual"').select((log2d) => {
    log2d.setAnglesLimits(minAngle, maxAngle);
  });
}
function setAlignment(plot, value) {
  const widget = plot.getRoot();
  from(widget).where('node => class(node) == "geotoolkit.welllog.Log2DVisual"').select((log2d) => {
    switch (value) {
      case "Left":
        log2d.setAlignment(ColumnAlignment.Left);
        break;
      case "Center":
        log2d.setAlignment(ColumnAlignment.Center);
        break;
      case "Table":
        log2d.setAlignment(ColumnAlignment.Table);
      case "Right":
        log2d.setAlignment(ColumnAlignment.Right);
        break;
    }
  });
}
function flipColors(plot) {
  const widget = plot.getRoot();
  from(widget).where('node => class(node) == "geotoolkit.welllog.Log2DVisual"').select((log2d) => {
    if (log2d.getColorProvider() instanceof DefaultColorProvider) {
      log2d.getColorProvider().reverse();
    }
  });
}
function toggleWrap(plot) {
  const widget = plot.getRoot();
  from(widget).where('node => class(node) == "geotoolkit.welllog.Log2DVisual"').select((log2d) => {
    log2d.setWrapInterpolation(!log2d.getWrapInterpolation());
  });
}
function exportToPdf(plot) {
  new Promise((resolve, reject) => {
    if (base64Font.length > 0) {
      resolve(null);
      return;
    }
    httpClient.get(font, {
      "responsetype": "blob"
    }).then((result) => {
      const reader = new FileReader();
      reader.readAsDataURL(result["data"]);
      reader.onloadend = () => {
        base64Font = reader.result.split(",")[1];
        resolve(null);
      };
      reader.onerror = reject;
    }, reject);
  }).then(() => {
    const widget = plot.getRoot();
    const limits = widget.getDepthLimits();
    widget.exportToPdf({
      "output": "Widget",
      "printsettings": {
        "scaling": ScalingOptions.FitWidth,
        "continuous": true
      },
      "embededfonts": [{
        "subtype": FontSubType.TrueType,
        "fontname": "roboto",
        "fontweight": "normal",
        "fontstyle": "normal",
        "fontbase64encodedfile": base64Font,
        "encoding": "Identity-H"
      }],
      "limits": {
        "start": limits.getLow(),
        "end": limits.getHigh()
      },
      "header": new HeaderComponent(500, 20),
      "footer": null,
      "imagecompression": {
        "mode": ImageCompression.PNG,
        "quality": 1,
        "speed": SpeedCompression.FAST
      },
      "imagescalefactor": 4
    });
  });
}
export { alignmentTypes, createScene, exportToPdf, flipColors, setAlignment, setAngle, setOffset, toggleWrap };

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

# Log2D Visual

Log2DVisualData is a simple data source that keeps all values and angles organized as a set of rows.

Offset

0

Flip Colors

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Log2DDataRow } from "@int/geotoolkit/welllog/data/Log2DDataRow.ts";
import { Log2DVisualData } from "@int/geotoolkit/welllog/data/Log2DVisualData.ts";
import { ScalingOptions } from "@int/geotoolkit/scene/exports/ScalingOptions.ts";
import { FontSubType } from "@int/geotoolkit/pdf/FontSubType.ts";
import { HeaderComponent } from "@int/geotoolkit/scene/exports/HeaderComponent.ts";
import { ImageCompression } from "@int/geotoolkit/pdf/ImageCompression.ts";
import { SpeedCompression } from "@int/geotoolkit/pdf/SpeedCompression.ts";
import { DefaultColorProvider } from "@int/geotoolkit/util/DefaultColorProvider.ts";
import { from } from "@int/geotoolkit/selection/from.ts";
import { Log2DVisual, PlotTypes } from "@int/geotoolkit/welllog/Log2DVisual.ts";
import { TrackType } from "@int/geotoolkit/welllog/TrackType.ts";
import { HeaderType } from "@int/geotoolkit/welllog/header/LogAxisVisualHeader.ts";
import { Orientation } from "@int/geotoolkit/util/Orientation.ts";
import { Range } from "@int/geotoolkit/util/Range.ts";
import { CompositeLog2DVisualHeader } from "@int/geotoolkit/welllog/header/CompositeLog2DVisualHeader.ts";
import { HttpClient } from "@int/geotoolkit/http/HttpClient.ts";
import { create2DVisual } from "/src/code/WellLog/utils/util.ts";
import data from "/src/code/WellLog/utils/log2DData.json?import";
import font from "/src/assets/fonts/roboto.ttf?import";
import { createWellLogWidget } from "/src/code/WellLog/utils/common.ts";
const httpClient = HttpClient.getInstance().getHttp();
let base64Font = "";
function generateData() {
  const log2dData = new Log2DVisualData();
  for (let i = 0; i < data.length; i++) {
    if (data[i].depth < 4780 || data[i].depth > 5040)
      continue;
    log2dData.getRows().push(new Log2DDataRow(data[i]["depth"], data[i]["values"], data[i]["angles"]));
  }
  log2dData.updateLimits();
  return log2dData;
}
function createScene(canvas) {
  const data2 = generateData();
  const widget = createWellLogWidget({
    "range": new Range(data2.getMinDepth(), data2.getMaxDepth())
  }).setOrientation(Orientation.Horizontal).setAxisHeaderType(HeaderType.Simple).scale(2);
  const headerProvider = widget.getHeaderContainer().getHeaderProvider();
  headerProvider.registerHeaderProvider(Log2DVisual.getClassName(), new CompositeLog2DVisualHeader());
  widget.addTrack(TrackType.IndexTrack);
  widget.addTrack(TrackType.LinearTrack).addChild([
    create2DVisual(data2, "Dataset #1", 0, "#7cb342", true).setPlotType(PlotTypes.Step)
  ]);
  widget.addTrack(TrackType.IndexTrack);
  widget.addTrack(TrackType.LinearTrack).addChild([
    create2DVisual(data2, "Dataset #2", 0, "#7cb342").setPlotType(PlotTypes.Linear)
  ]);
  widget.addTrack(TrackType.IndexTrack);
  return new Plot({
    "canvaselement": canvas,
    "root": widget
  });
}
function setOffset(plot, value) {
  const widget = plot.getRoot();
  from(widget).where('node => class(node) == "geotoolkit.welllog.Log2DVisual"').select((log2d) => {
    log2d.setOffsets(value * Math.PI / 180);
  });
}
function flipColors(plot) {
  const widget = plot.getRoot();
  from(widget).where('node => class(node) == "geotoolkit.welllog.Log2DVisual"').select((log2d) => {
    if (log2d.getColorProvider() instanceof DefaultColorProvider) {
      log2d.getColorProvider().reverse();
    }
  });
}
function exportToPdf(plot) {
  new Promise((resolve, reject) => {
    if (base64Font.length > 0) {
      resolve(null);
      return;
    }
    httpClient.get(font, {
      "responsetype": "blob"
    }).then((result) => {
      const reader = new FileReader();
      reader.readAsDataURL(result["data"]);
      reader.onloadend = () => {
        base64Font = reader.result.split(",")[1];
        resolve(null);
      };
      reader.onerror = reject;
    }, reject);
  }).then(() => {
    const widget = plot.getRoot();
    const limits = widget.getDepthLimits();
    widget.exportToPdf({
      "output": "Widget",
      "printsettings": {
        "scaling": ScalingOptions.FitWidth,
        "continuous": true
      },
      "embededfonts": [{
        "subtype": FontSubType.TrueType,
        "fontname": "roboto",
        "fontweight": "normal",
        "fontstyle": "normal",
        "fontbase64encodedfile": base64Font,
        "encoding": "Identity-H"
      }],
      "limits": {
        "start": limits.getLow(),
        "end": limits.getHigh()
      },
      "header": new HeaderComponent(500, 20),
      "footer": null,
      "imagecompression": {
        "mode": ImageCompression.PNG,
        "quality": 1,
        "speed": SpeedCompression.FAST
      },
      "imagescalefactor": 4
    });
  });
}
export { createScene, exportToPdf, flipColors, setOffset };

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

# NaN Values and Cut-off Values

The following code shows how to create a log2d NaN (Not a Number) values and cut-off values.

  • The orange curve is displayed without NaN values on top of Log2D with same values for each column. The other curves have NaN values in depths: 27, 30, 35 and the GapFillCutoffValue is specified as 20.
  • The blue curve displays NaN values as gap.
  • The green curve enables an option to specify cutoff for NaN values by depths. If depth interval is less than specified cutoff value, which is 20 in this example, then interpolation is applied in such a way that the interval is connected and NaN values between are removed.
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { CompositeLog2DVisualHeader } from "@int/geotoolkit/welllog/header/CompositeLog2DVisualHeader.ts";
import { LogCurve } from "@int/geotoolkit/welllog/LogCurve.ts";
import { LogData } from "@int/geotoolkit/welllog/data/LogData.ts";
import { ArrayLogDataSource } from "@int/geotoolkit/welllog/data/ArrayLogDataSource.ts";
import { DataTable } from "@int/geotoolkit/data/DataTable.ts";
import { Log2DVisual, PlotTypes } from "@int/geotoolkit/welllog/Log2DVisual.ts";
import { TrackType } from "@int/geotoolkit/welllog/TrackType.ts";
import { HeaderType } from "@int/geotoolkit/welllog/header/LogAxisVisualHeader.ts";
import { Range } from "@int/geotoolkit/util/Range.ts";
import { create2DVisual } from "/src/code/WellLog/utils/util.ts";
import { createWellLogWidget } from "/src/code/WellLog/utils/common.ts";
function generateData(withNaN) {
  const table = new DataTable({
    "cols": [{
      "name": "depth",
      "type": "number"
    }, {
      "name": "val1",
      "type": "number"
    }, {
      "name": "val2",
      "type": "number"
    }, {
      "name": "val3",
      "type": "number"
    }, {
      "name": "val4",
      "type": "number"
    }, {
      "name": "val5",
      "type": "number"
    }]
  });
  const depths = [10, 12, 20, 27, 30, 35, 40, 42, 50, 55, 60, 67, 70, 75];
  const values = withNaN ? [75, 100, 90, NaN, NaN, NaN, 60, 75, 80, 100, 40, 67, 40, 80] : [75, 100, 90, 50, 60, 95, 60, 75, 80, 100, 40, 67, 40, 80];
  values.forEach((val, i) => {
    table.addRow([depths[i], val, val, val, val, val]);
  });
  return new ArrayLogDataSource({
    "datatable": table,
    "angles": {
      "values": [0, Math.PI / 2, Math.PI, Math.PI * 3 / 2, Math.PI * 2]
    }
  });
}
function createLogCurve(withNaN, color) {
  const data = new LogData({
    "name": "First Column",
    "depths": [10, 12, 20, 27, 30, 35, 40, 42, 50, 55, 60, 67, 70, 75],
    "values": withNaN ? [75, 100, 90, NaN, NaN, NaN, 60, 75, 80, 100, 40, 67, 40, 80] : [75, 100, 90, 50, 60, 95, 60, 75, 80, 100, 40, 67, 40, 80],
    "indexunit": "ft"
  });
  return new LogCurve(data).setLineStyle({
    "color": color,
    "width": 2
  });
}
function createScene(canvas) {
  const data = generateData(false);
  const widget = createWellLogWidget({
    "range": new Range(data.getMinDepth(), data.getMaxDepth())
  }).setAxisHeaderType(HeaderType.Simple);
  const headerProvider = widget.getHeaderContainer().getHeaderProvider();
  headerProvider.registerHeaderProvider(Log2DVisual.getClassName(), new CompositeLog2DVisualHeader());
  widget.addTrack(TrackType.IndexTrack);
  widget.addTrack(TrackType.LinearTrack).addChild([
    create2DVisual(data, "No Null", 0, "#fff9c4", true).setPlotType(PlotTypes.Step),
    createLogCurve(false, "#ef6c00")
  ]);
  widget.addTrack(TrackType.IndexTrack);
  let dataWithNullValues = generateData(true);
  widget.addTrack(TrackType.LinearTrack).addChild([
    create2DVisual(dataWithNullValues, "Null", 0, "#fff9c4", true).setPlotType(PlotTypes.Step),
    createLogCurve(true, "#2196f3")
  ]);
  widget.addTrack(TrackType.IndexTrack);
  dataWithNullValues = generateData(true);
  widget.addTrack(TrackType.LinearTrack).addChild([
    create2DVisual(dataWithNullValues, "Gap Fill", 0, "#fff9c4", true).setPlotType(PlotTypes.Step).setGapFillCutoffValue(20),
    createLogCurve(true, "#7cb342").setGapFillCutoffValue(20)
  ]);
  const plot = new Plot({
    "canvaselement": canvas,
    "root": widget
  });
  widget.setDepthLimits(5, 80).setHeaderHeight("auto").fitToHeight();
  return plot;
}
export { createScene };

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