This tutorial demonstrates how to share single well template for different well tracks and instantiate it when needed, to continue to work with real instance when needed.
This tutorial assumes familiarity with the Proxy track tutorial tutorials.
# Create Proxy Track
In the next step, create proxy well track and provide created template as prototype.
# Result
This WellLog–Multi Well Correlation widget can display well tracks and correlation tracks between them. A Well Track can have the different set of tracks and curves, which is named as a template. Often the same template is used for all wells in the display to see correlation between the different wells. In this case usage of a normal WellTrack cannot be efficient for big amount of wells, because it has a copy of tracks and curves and the own set of tools. In this case it is better to use ProxyWellTrack for read only displays.
import { obfuscate } from "@int/geotoolkit/lib.js";
import { MultiWellWidget } from "@int/geotoolkit/welllog/multiwell/MultiWellWidget.ts";
import { ProxyTrackActivationTool } from "@int/geotoolkit/welllog/multiwell/tools/ProxyTrackActivationTool.ts";
import { Range } from "@int/geotoolkit/util/Range.ts";
import { TrackType as MultiWellTrackType } from "@int/geotoolkit/welllog/multiwell/TrackType.ts";
import { TrackType as WellLogTrackType } from "@int/geotoolkit/welllog/TrackType.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { MathUtil } from "@int/geotoolkit/util/MathUtil.ts";
import { LogCurve } from "@int/geotoolkit/welllog/LogCurve.ts";
import { LineStyle } from "@int/geotoolkit/attributes/LineStyle.ts";
import { LogMarker } from "@int/geotoolkit/welllog/LogMarker.ts";
import { AlignmentStyle, TextStyle } from "@int/geotoolkit/attributes/TextStyle.ts";
import { AnchorType } from "@int/geotoolkit/util/AnchorType.ts";
import { CorrelationMarker } from "@int/geotoolkit/welllog/multiwell/correlation/CorrelationMarker.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { LogData } from "@int/geotoolkit/welllog/data/LogData.ts";
import { DataBinding } from "@int/geotoolkit/data/DataBinding.ts";
import { DataSource } from "@int/geotoolkit/data/DataSource.ts";
import { DataBindingRegistry } from "@int/geotoolkit/data/DataBindingRegistry.ts";
import { Events as ProxyWellTrackEvents } from "@int/geotoolkit/welllog/multiwell/ProxyWellTrack.ts";
import { getCurvesData } from "/src/code/WellLog/MultiWell/data/curves.ts";
import { Node, StateChanges } from "@int/geotoolkit/scene/Node.ts";
import { ResponsiveStyle } from "@int/geotoolkit/responsive/ResponsiveStyle.ts";
const curvesData = getCurvesData();
const bindingFunctions = [];
class CurveBinding extends DataBinding {
constructor() {
super();
}
accept(node) {
return node instanceof LogCurve;
}
bind(curve, data) {
if (data == null) {
return;
}
const id = curve.getName();
const source = data.getCurveSource(id);
if (source != null) {
const limits = MathUtil.calculateNeatLimits(source.getMinValue(), source.getMaxValue());
if (curve.isCustomLimits() === true) {
curve.setData(source, false, true);
} else {
curve.setData(source, true, true).setNormalizationLimits(limits.getLow(), limits.getHigh());
}
}
}
unbind(curve, data) {
}
}
obfuscate(CurveBinding);
class LogDataSource extends DataSource {
constructor(startDepth) {
super();
this._sources = {};
this._startDepth = startDepth;
this._step = 10;
}
getCurveSource(curveMnemonic) {
if (this._sources[curveMnemonic]) {
return this._sources[curveMnemonic];
}
const data = new LogData(curveMnemonic);
const depths = [];
const values = [];
const curveData = getCurveData(curveMnemonic);
const amountOfPoints = curveData.length;
for (let i = 0; i < amountOfPoints; i++) {
depths.push(i * this._step + this._startDepth);
values.push(curveData[i]);
}
data.setValues(depths, values);
this._sources[curveMnemonic] = data;
return data;
}
}
obfuscate(LogDataSource);
function getCurveData(curveMnemonic) {
const curveNames = curvesData.curveNames;
const curveData = curvesData.curveData;
for (let i = 0; i < curveNames.length; i++) {
if (curveNames[i] === curveMnemonic)
return curveData[i];
}
return null;
}
function createCorrelation(widget, width, wells) {
const correlation = widget.createTrack(MultiWellTrackType.CorrelationTrack, {
"width": width
});
wells.push(correlation);
return correlation;
}
let _wellIndex = 1;
function createWell(widget, positions, depthrange, template, wells) {
const title = "Well " + _wellIndex++;
const well = widget.createTrack(MultiWellTrackType.ProxyTrack, {
"range": positions,
"welllog": {
"range": depthrange
},
"width": template.getLayoutStyle().getWidth(),
"height": template.getLayoutStyle().getHeight(),
"prototype": {
"template": template
},
"tools": {
"navigation": true
},
"title": title,
"name": title
}).on(ProxyWellTrackEvents.Mount, (eventType, sender) => {
const data = new LogDataSource(depthrange.getLow());
sender.setData(data);
});
wells.push(well);
return well;
}
function getRules() {
const EPSILON = 1e-9;
return [
{
condition: function(node) {
const transform = node.getSceneTransform();
return Math.abs(transform.getScaleX() + EPSILON) < 1;
},
restore: false,
css: `
*[cssclass="INDEX_TRACK"] {
visible: false;
}
.geotoolkit.welllog.header.LogVisualHeader {
visible: false;
}
.geotoolkit.welllog.multiwell.WellTrackVisualHeader {
visible: true;
}
*[cssclass="horizontalGrid"] {
visible: false;
}
*[cssclass="verticalGrid"] {
visible: false;
}
.geotoolkit.welllog.LogTrack {
border-visible: true;
}
.geotoolkit.welllog.LogMarker {
visiblenamelabel: false;
visibledepthlabel: false;
}
`
},
{
condition: function(node) {
const transform = node.getSceneTransform();
return Math.abs(transform.getScaleX() + EPSILON) >= 1;
},
restore: false,
css: `
*[cssclass="INDEX_TRACK"] {
visible: true;
}
.geotoolkit.welllog.header.LogVisualHeader {
visible: true;
}
.geotoolkit.welllog.multiwell.WellTrackVisualHeader {
visible: true;
}
*[cssclass="horizontalGrid"] {
visible: true;
}
*[cssclass="verticalGrid"] {
visible: true;
}
.geotoolkit.welllog.LogTrack {
border-visible: true;
}
.geotoolkit.welllog.LogMarker {
visiblenamelabel: true;
visibledepthlabel: true;
}
`
}
];
}
function setLevelOfDetails(widget, template) {
widget.getTrackContainer().setResponsiveStyle(new ResponsiveStyle({
"rules": getRules(),
"target": widget,
"start": function() {
Node.enableSceneGraphNotification(false);
},
"end": function() {
Node.enableSceneGraphNotification(true);
template.updateState(void 0, StateChanges.Rebuild);
widget.updateState(void 0, StateChanges.Rebuild);
widget.getTrackContainer().updateLayout();
}
}));
return widget;
}
function createWidget() {
const widget = new MultiWellWidget({
"tools": {
"cursortracking": {
"tooltip": {
"enabled": true
}
}
}
});
widget.getTool().insert(0, new ProxyTrackActivationTool(widget));
const template = createTemplate(widget);
const wells = [];
const bindingFunc = new CurveBinding();
bindingFunctions.push(bindingFunc);
const dataBinding = DataBindingRegistry.getInstance();
dataBinding.add(bindingFunc);
widget.setDataBinding(dataBinding);
for (let i = 0; i < 333; i++) {
let correlation3 = null;
if (i !== 0) {
correlation3 = createCorrelation(widget, 50, wells);
}
const well1 = createWell(widget, new Range(0, 500), new Range(4500, 5e3), template, wells);
addTops(well1, "Tarbert", 4750, KnownColors.DarkRed);
if (i !== 0) {
addTopsCorrelation(correlation3, 4800, 4750, KnownColors.DarkRed);
}
const correlation1 = createCorrelation(widget, 50, wells);
const well2 = createWell(widget, new Range(50, 300), new Range(2500, 5e3), template, wells);
addTops(well2, "Tarbert", 3e3, KnownColors.DarkRed);
const correlation2 = createCorrelation(widget, 50, wells);
const well3 = createWell(widget, new Range(25, 400), new Range(4700, 5e3), template, wells);
addTops(well3, "Tarbert", 4800, KnownColors.DarkRed);
addTopsCorrelation(correlation1, 4750, 3e3, KnownColors.DarkRed);
addTopsCorrelation(correlation2, 3e3, 4800, KnownColors.DarkRed);
}
widget.addTrack(wells);
setLevelOfDetails(widget, template);
return widget;
}
function createTemplate(widget) {
const well = widget.createTrack(MultiWellTrackType.WellTrack, {
"welllog": {
"range": new Range(4700, 5e3)
}
});
well.addTrack(WellLogTrackType.IndexTrack);
well.addTrack(WellLogTrackType.LinearTrack).addChild([
createEmptyCurve("GR").setLineStyle(KnownColors.Green),
createEmptyCurve("CALI").setLineStyle(KnownColors.Orange)
]);
return well;
}
function createEmptyCurve(name) {
return new LogCurve().setName(name);
}
function addTops(well, name, depth, color) {
const top = new LogMarker(depth);
top.setLineStyle(LineStyle.fromObject({ "color": color }));
top.setTextStyle(TextStyle.fromObject({
"color": color,
"alignment": AlignmentStyle.Left,
"font": "12px sans-serif"
}));
top.setNameLabel(name);
top.setNameLabelPosition(AnchorType.TopCenter);
top.setDepthLabel(depth + "");
top.setDepthLabelPosition(AnchorType.BottomCenter);
well.getMarkerLayer().addChild(top);
}
function addTopsCorrelation(track, leftDepth, rightDepth, color) {
track.addChild(new CorrelationMarker({
"linestyle": {
"color": color,
"width": 2,
"pixelsnapmode": { "x": true, "y": true }
},
"leftdepth": leftDepth,
"rightdepth": rightDepth
}));
}
function createScene(canvas) {
const widget = createWidget();
const plot = new Plot({
"canvaselement": canvas,
"root": widget
});
widget.setHeaderHeight("auto");
return {
plot,
widget
};
}
function dispose() {
bindingFunctions.forEach((bindingFunc) => DataBindingRegistry.getInstance().remove(bindingFunc));
}
export { createScene, dispose };
createScene(document.querySelector('[ref="plot"]'));