The Depth Shifting tool is a specialized manipulator used to edit the curve depth shifting points.
# Simple Depth Shifting
This example displays all the functionalities of the Curve Depth Mapping tool. The left curve is a regular curve and the right curve is a curve with Depth Mapping.
Both curves has exactly the same Data Source instance.
The Depth Mapping toolbar buttons enable/disable the three editing modes:
- Edit Mode - Allows dragging the selected depth-mapping marker along the track, changing related depth range transformation
- Insert Mode - Allows inserting a depth-mapping marker into the log curve at the depth at which the click occurred on the track. While dragging the mouse along the track, a host marker will be drawn across to show the corresponding depth.
- Delete Mode - Allows deleting the selected depth-mapping marker by clicking.
Depth Mapping tool is active at the begining but you need to add at least one shift point to start.
import { EventDispatcher } from "@int/geotoolkit/util/EventDispatcher.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { LogCurve } from "@int/geotoolkit/welllog/LogCurve.ts";
import { HeaderType as LogAxisVisualHeaderHeaderType } from "@int/geotoolkit/welllog/header/LogAxisVisualHeader.ts";
import { TrackType as WellLogTrackType } from "@int/geotoolkit/welllog/TrackType.ts";
import { Layer } from "@int/geotoolkit/scene/Layer.ts";
import { LogCurveDataSource } from "@int/geotoolkit/welllog/data/LogCurveDataSource.ts";
import { NumericalDataSeries } from "@int/geotoolkit/data/NumericalDataSeries.ts";
import { DataTable } from "@int/geotoolkit/data/DataTable.ts";
import { CSVWriter } from "@int/geotoolkit/data/CSVWriter.ts";
import { TextStream } from "@int/geotoolkit/util/stream/TextStream.ts";
import { DepthShiftingTool } from "@int/geotoolkit/welllog/widgets/tools/DepthShiftingTool.ts";
import { Events as EditorEvents } from "@int/geotoolkit/controls/editing/Events.ts";
import { from } from "@int/geotoolkit/selection/from.ts";
import { Events as ToolEvents } from "@int/geotoolkit/controls/tools/AbstractTool.ts";
import { LogData } from "@int/geotoolkit/welllog/data/LogData.ts";
import { HoldTitle } from "@int/geotoolkit/welllog/header/HoldTitle.ts";
import { Elements } from "@int/geotoolkit/welllog/header/AdaptiveLogCurveVisualHeader.ts";
import { Sections } from "@int/geotoolkit/welllog/header/AdaptiveLogVisualHeader.ts";
import { LasWriter } from "/src/code/WellLog/utils/lasWriter.ts";
import { createWellLogWidget } from "/src/code/WellLog/utils/common.ts";
import { curveData } from "/src/code/WellLog/utils/curveData.ts";
export class DepthShifting extends EventDispatcher {
constructor(canvas) {
super();
this._widget = this.initializeWidget();
this._plot = this.addWidgetToCanvas(canvas, this._widget);
const logCurve = from(this._widget).where((node) => node instanceof LogCurve).selectFirst();
this._depthShiftingTool = this.createDepthShiftingTool(this._widget).setHandleStyles({ "ghostlinestyle": "blue" }).setShape(logCurve);
const curves = from(this._widget.getTrack(3)).where((node) => node instanceof LogCurve).where((curve) => curve.getParent() instanceof Layer).selectToArray();
const synchronize = () => {
const depths = this.getShiftedDepths();
if (depths == null) {
return;
}
for (let i = 0; i < curves.length; i++) {
const curve = curves[i];
const logDataSource = new LogCurveDataSource().setData({
depths,
values: curve.getDataSource().getValues()
}).setName(curve.getName());
curve.setData(logDataSource);
}
};
const restore = () => {
for (let i = 0; i < curves.length; i++) {
const curve = curves[i];
const originalCurve = curve.getProperty("original");
const logDataSource = new LogCurveDataSource().setData({
depths: originalCurve.getDataSource().getDepths(),
values: curve.getDataSource().getValues()
}).setName(curve.getName());
curve.setData(logDataSource);
}
};
synchronize();
this._depthShiftingTool.on(EditorEvents.Insert, synchronize).on(EditorEvents.Delete, synchronize).on(EditorEvents.Dragging, synchronize).on(EditorEvents.DragEnd, synchronize).on(EditorEvents.Clear, restore);
}
dispose() {
if (this._widget != null) {
this._widget.dispose();
this._widget = null;
}
if (this._plot != null) {
this._plot.dispose();
this._plot = null;
}
}
customizeCurveHeader(widget) {
const headerProvider = widget.getHeaderContainer().getHeaderProvider();
const header = headerProvider.getHeaderProvider(LogCurve.getClassName());
const customHeder = header.clone();
customHeder.setElement({
[Elements.ScaleFrom]: {
"section": Sections.Top
},
[Elements.ScaleTo]: {
"section": Sections.Top
},
[Elements.Line]: {
"section": Sections.Top
}
});
customHeder.setSection({
[Sections.Top]: {
"paddingstyle": "5px 5px 5px"
},
[Sections.Middle]: {
"visible": false,
"size": 0
},
[Sections.Bottom]: {
"visible": false,
"size": 0
}
});
headerProvider.registerHeaderProvider(LogCurve.getClassName(), customHeder);
}
initializeWidget() {
const widget = createWellLogWidget({
"track": {
"header": {
"titlefirst": false,
"firsttolast": true,
"toptobottom": false,
"holdtitle": HoldTitle.Top
}
}
}).setAxisHeaderType(LogAxisVisualHeaderHeaderType.Simple);
this.customizeCurveHeader(widget);
widget.addTrack(WellLogTrackType.IndexTrack);
widget.addTrack(WellLogTrackType.LinearTrack).addChild([
this.createCurve("CALI").setName("CALI - orig").setLineStyle({ "pattern": [2, 2] }, true)
]);
widget.addTrack(WellLogTrackType.AnnotationTrack).setWidth(40);
const shiftCurves = [
this.createCurve("RHOB").setLineStyle("#404B97"),
this.createCurve("DLT").setLineStyle("#76EAE8"),
this.createCurve("SP").setLineStyle("#8EE48D"),
this.createCurve("CALI").setLineStyle("#7E5381")
];
widget.addTrack(WellLogTrackType.LinearTrack).addChild(shiftCurves.map(
(curve) => curve.setLineStyle({ "pattern": [2, 2] }, true).setOpacity(0.5)
)).addChild(
new Layer().addChild(shiftCurves.map(
(curve) => curve.clone().setProperty("original", curve).setLineStyle({ "pattern": null }, true).setOpacity(1)
))
);
widget.addTrack(WellLogTrackType.IndexTrack);
const logCurve = from(widget).where((node) => node instanceof LogCurve).selectFirst();
widget.setDepthLimits(logCurve.getModelLimits().getTop(), logCurve.getModelLimits().getBottom());
return widget;
}
getEditMode() {
return this._depthShiftingTool != null ? this._depthShiftingTool.getMode() : null;
}
addWidgetToCanvas(canvas, widget) {
const plot = new Plot({
"canvaselement": canvas,
"root": widget
});
widget.setVisibleDepthLimits(1e4, 12e3);
widget.setHeaderHeight("auto");
return plot;
}
createDepthShiftingTool(widget) {
const manipulatorLayer = new Layer({
id: "curves.manipulator.layer"
});
widget.getTrackManipulatorLayer().addChild(manipulatorLayer);
const depthShiftingTool = new DepthShiftingTool({
"layer": manipulatorLayer,
"handlestyles": {
"activelinestyle": {
"color": "blue"
},
"ghostlinestyle": {
"color": "red"
},
"depthlabelfillstyle": "#D8F2D7",
"depthlabelpaddingstyle": "5px 8px 2px"
}
});
const tools = widget.getTool();
const toolTip = tools.getToolByName("tooltip");
depthShiftingTool.on(ToolEvents.onStateChanged, (event, tool) => {
this.notify(ToolEvents.onStateChanged, this);
toolTip.setEnabled(tool.isActive() === false);
});
tools.getToolByName("cross-hair").getParentTool().add(depthShiftingTool);
return depthShiftingTool;
}
createTestData(dataMnemonic, from2, step) {
from2 = from2 || 4500;
step = step || 10;
const depths = [];
const values = [];
const curveDat = curveData[dataMnemonic];
const amountOfPoints = curveDat.length;
for (let i = 0; i < amountOfPoints; i++) {
depths.push(i * step + from2);
values.push(curveDat[i]);
}
return new LogData({
"name": dataMnemonic,
"depths": depths,
"values": values
});
}
createCurve(dataSource) {
if (typeof dataSource === "string") {
dataSource = this.createTestData(dataSource);
}
return new LogCurve(dataSource, true).setId(dataSource.getName()).setName(dataSource.getName()).setLineStyle({
"color": KnownColors["Blue"],
"width": 2
});
}
getShiftedDepths() {
const logCurve = this._depthShiftingTool.getShape();
if (logCurve == null) {
return null;
}
const originalDepths = logCurve.getDataSource().getDepths();
const depthLimits = this._depthShiftingTool.getRange();
const mapping = this._depthShiftingTool.getDepthMappings();
return DepthShiftingTool.mapDepths(depthLimits, mapping, originalDepths);
}
getDataSourcesToExport() {
const originalCurves = from(this._widget.getTrack(3)).where((node) => node instanceof LogCurve).where((curve) => curve.getParent() instanceof Layer === false).selectToArray();
return originalCurves.map((curve) => curve.getDataSource());
}
getExportDataTable() {
const depths = this.getShiftedDepths();
if (depths == null) {
return null;
}
const depthSeries = new NumericalDataSeries({
id: "depths",
name: "depths",
unit: "ft",
data: depths
});
const columns = [depthSeries];
this.getDataSourcesToExport().forEach((data) => {
const valueSeries = new NumericalDataSeries({
id: data.getName(),
name: data.getName(),
unit: data.getValueUnit(),
data: data.getValues()
});
columns.push(valueSeries);
});
return new DataTable({
"cols": columns,
"meta": {
"index": 0
}
});
}
saveToLAS() {
const table = this.getExportDataTable();
if (table == null) {
return;
}
new LasWriter().save(table, "example.las");
}
exportToCSV() {
const table = this.getExportDataTable();
if (table == null) {
return;
}
new CSVWriter({
"stream": new TextStream({
"filename": "csvfile.csv",
"type": "text/csv",
"save": true
})
}).writeTable(table, {
"includeheaders": true
}).close();
}
setMode(mode) {
this._depthShiftingTool.setMode(mode);
return this;
}
setEnable(enable) {
this._depthShiftingTool.setEnabled(enable);
if (enable) {
const logCurve = from(this._widget).where((node) => node instanceof LogCurve).selectFirst();
this._depthShiftingTool.setShape(logCurve);
} else {
this._depthShiftingTool.setShape(null);
this._depthShiftingTool.clearDepthMapping();
}
return this;
}
}
function createScene(canvas) {
return new DepthShifting(canvas);
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Advanced Depth Shifting
Depth Mapping tool is activated at second LogTrack.