@webviz/subsurface-viewer
Version:
3D visualization component for subsurface reservoir data
895 lines • 38.9 kB
JavaScript
import _, { isEmpty, isEqual } from "lodash";
import { distance, dot, subtract } from "mathjs";
import { interpolateNumberArray } from "d3";
import { CompositeLayer } from "@deck.gl/core";
import { PathStyleExtension } from "@deck.gl/extensions";
import { GeoJsonLayer, PathLayer } from "@deck.gl/layers";
import { GL } from "@luma.gl/constants";
import { getColors, rgbValues } from "@emerson-eps/color-tables/";
import { createPropertyData, getLayersById, isDrawingEnabled, } from "../utils/layerTools";
import { WellLabelLayer } from "./layers/wellLabelLayer";
import { abscissaTransform } from "./utils/abscissaTransform";
import { GetBoundingBox, checkWells, coarsenWells, invertPath, splineRefine, } from "./utils/spline";
import { getColor, getTrajectory } from "./utils/trajectory";
export var SubLayerId;
(function (SubLayerId) {
SubLayerId["COLORS"] = "colors";
SubLayerId["SIMPLE"] = "simple";
SubLayerId["OUTLINE"] = "outline";
SubLayerId["HIGHLIGHT"] = "highlight";
SubLayerId["HIGHLIGHT_2"] = "highlight2";
SubLayerId["LOG_CURVE"] = "log_curve";
SubLayerId["SELECTION"] = "selection";
SubLayerId["LABELS"] = "labels";
})(SubLayerId || (SubLayerId = {}));
const defaultProps = {
"@@type": "WellsLayer",
name: "Wells",
id: "wells-layer",
autoHighlight: true,
opacity: 1,
lineWidthScale: 1,
pointRadiusScale: 1,
lineStyle: { dash: false },
outline: true,
logRadius: 10,
logCurves: true,
refine: false,
visible: true,
selectedWell: "@@#editedData.selectedWells", // used to get data from deckgl layer
depthTest: true,
ZIncreasingDownwards: true,
simplifiedRendering: false,
section: false,
dataTransform: coarsenWells,
// Deprecated props
wellNameColor: [0, 0, 0, 255],
wellNameSize: 10,
};
const LINE = "line";
const POINT = "point";
const DEFAULT_POINT_SIZE = 8;
const DEFAULT_LINE_WIDTH = 5;
const DEFAULT_DASH = [5, 5];
export function getSize(type, accessor, offset = 0) {
if (typeof accessor == "function") {
return (object) => {
return (accessor(object) + offset);
};
}
if (accessor == 0)
return 0;
if (accessor > 0)
return accessor + offset;
if (type == LINE)
return DEFAULT_LINE_WIDTH + offset;
if (type == POINT)
return DEFAULT_POINT_SIZE + offset;
return 0;
}
export default class WellsLayer extends CompositeLayer {
recomputeDataState() {
const data = this.props.data;
const refine = this.props.refine;
const doRefine = typeof refine === "number" ? refine > 1 : refine;
const stepCount = typeof refine === "number" ? refine : 5;
if (!dataIsReady(data)) {
return;
}
let transformedData = data;
if (this.props.ZIncreasingDownwards) {
transformedData = invertPath(transformedData);
}
if (this.props.section) {
transformedData = abscissaTransform(transformedData);
}
// Mutate data to remove duplicates
checkWells(transformedData);
// Conditionally apply spline interpolation to refine the well path.
if (doRefine) {
transformedData = splineRefine(transformedData, stepCount);
}
this.setState({ data: transformedData });
}
shouldUpdateState({ changeFlags }) {
return (changeFlags.viewportChanged ||
changeFlags.propsOrDataChanged ||
typeof changeFlags.updateTriggersChanged === "object");
}
updateState({ props, oldProps }) {
const needs_reload = !isEqual(props.data, oldProps.data) ||
!isEqual(props.ZIncreasingDownwards, oldProps.ZIncreasingDownwards) ||
!isEqual(props.refine, oldProps.refine);
if (needs_reload) {
this.recomputeDataState();
}
}
onClick(info) {
var _a;
// Make selection only when drawing is disabled
if (isDrawingEnabled(this.context.layerManager)) {
return false;
}
else {
this.context.userData.setEditedData({
selectedWell: (_a = info.object) === null || _a === void 0 ? void 0 : _a.properties.name,
});
return false; // do not return true to allow DeckGL props.onClick to be called
}
}
setSelection(well, _selection) {
if (this.internalState) {
this.setState({
well: well,
selection: _selection,
});
}
}
setMultiSelection(wells) {
if (this.internalState) {
this.setState({
selectedMultiWells: wells,
});
}
}
getWellDataState() {
return this.state.data;
}
getLegendData(value) {
return getLegendData(value, "", this.props.logName, this.props.logColor);
}
setLegend(value) {
this.setState({
legend: this.getLegendData(value),
});
}
getLogLayer() {
var _a;
const sub_layers = (_a = this.internalState) === null || _a === void 0 ? void 0 : _a.subLayers;
const log_layer = getLayersById(sub_layers, "wells-layer-log_curve");
return log_layer === null || log_layer === void 0 ? void 0 : log_layer[0];
}
getSelectionLayer() {
var _a;
const sub_layers = (_a = this.internalState) === null || _a === void 0 ? void 0 : _a.subLayers;
const log_layer = getLayersById(sub_layers, "wells-layer-selection");
return log_layer === null || log_layer === void 0 ? void 0 : log_layer[0];
}
getLogCurveData() {
const log_layer = this.getLogLayer();
return log_layer === null || log_layer === void 0 ? void 0 : log_layer.props.data;
}
setupLegend() {
const data = this.getLogCurveData();
if (data)
this.setLegend(data);
}
getWellLabelPosition() {
var _a;
if ((_a = this.props.wellLabel) === null || _a === void 0 ? void 0 : _a.getPositionAlongPath) {
return this.props.wellLabel.getPositionAlongPath;
}
else if (!_.isUndefined(this.props.wellNameAtTop)) {
// Backwards compatibility for `wellNameAtTop` prop.
if (typeof this.props.wellNameAtTop === "boolean") {
if (this.props.wellNameAtTop) {
return 0;
}
// if wellNameAtTop is false, then place name at bottom.
return 1;
}
// `wellNameAtTop` can also be a number [0, 100] indicating the
// percentage along the trajectory from the top.
return this.props.wellNameAtTop / 100;
}
return 0;
}
createWellLabelLayer(data) {
var _a, _b, _c, _d, _e;
if (!this.props.wellLabel && !this.props.wellNameVisible) {
return null;
}
const parameters = {
[GL.DEPTH_TEST]: this.props.depthTest,
[GL.POLYGON_OFFSET_FILL]: true,
};
const fastDrawing = this.props.simplifiedRendering;
const wellLabelProps = this.getSubLayerProps(Object.assign(Object.assign({}, this.props.wellLabel), { id: SubLayerId.LABELS, data, pickable: true,
// Z is always increasing upwards at this stage
zIncreasingDownwards: false, getPositionAlongPath: this.getWellLabelPosition(), getColor: (_b = (_a = this.props.wellLabel) === null || _a === void 0 ? void 0 : _a.getColor) !== null && _b !== void 0 ? _b : this.props.wellNameColor, getAnchor: "start", getSize: (_d = (_c = this.props.wellLabel) === null || _c === void 0 ? void 0 : _c.getSize) !== null && _d !== void 0 ? _d : this.props.wellNameSize, parameters, visible: !fastDrawing, background: ((_e = this.props.wellLabel) === null || _e === void 0 ? void 0 : _e.background) ||
this.props.hideOverlappingWellNames }));
return new WellLabelLayer(wellLabelProps);
}
renderLayers() {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
const data = this.getWellDataState();
const positionFormat = "XYZ";
const isDashed = !!((_a = this.props.lineStyle) === null || _a === void 0 ? void 0 : _a.dash);
if (!data || !(data === null || data === void 0 ? void 0 : data.features.length)) {
return [];
}
const extensions = [
new PathStyleExtension({
dash: isDashed,
highPrecisionDash: isDashed,
}),
];
const parameters = {
[GL.DEPTH_TEST]: this.props.depthTest,
[GL.POLYGON_OFFSET_FILL]: true,
};
// Reduced details when rotating or panning the view if "fastDrawing " is set.
const fastDrawing = this.props.simplifiedRendering;
const defaultLayerProps = {
data,
pickable: false,
stroked: false,
positionFormat,
pointRadiusUnits: "pixels",
lineWidthUnits: "pixels",
pointRadiusScale: this.props.pointRadiusScale,
lineWidthScale: this.props.lineWidthScale,
getLineWidth: getSize(LINE, (_b = this.props.lineStyle) === null || _b === void 0 ? void 0 : _b.width, -1),
getPointRadius: getSize(POINT, (_c = this.props.wellHeadStyle) === null || _c === void 0 ? void 0 : _c.size, -1),
lineBillboard: true,
pointBillboard: true,
parameters,
visible: fastDrawing,
};
const colorsLayerProps = this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { id: SubLayerId.COLORS, pickable: true, extensions, getDashArray: getDashFactor((_d = this.props.lineStyle) === null || _d === void 0 ? void 0 : _d.dash, getSize(LINE, (_e = this.props.lineStyle) === null || _e === void 0 ? void 0 : _e.width), -1), visible: !fastDrawing, getLineColor: getColor((_f = this.props.lineStyle) === null || _f === void 0 ? void 0 : _f.color), getFillColor: getColor((_g = this.props.wellHeadStyle) === null || _g === void 0 ? void 0 : _g.color) }));
const fastLayerProps = this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { id: SubLayerId.SIMPLE, positionFormat, getLineColor: getColor((_h = this.props.lineStyle) === null || _h === void 0 ? void 0 : _h.color), getFillColor: getColor((_j = this.props.wellHeadStyle) === null || _j === void 0 ? void 0 : _j.color) }));
const outlineLayerProps = this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { id: SubLayerId.OUTLINE, getLineWidth: getSize(LINE, (_k = this.props.lineStyle) === null || _k === void 0 ? void 0 : _k.width), getPointRadius: getSize(POINT, (_l = this.props.wellHeadStyle) === null || _l === void 0 ? void 0 : _l.size), extensions, getDashArray: getDashFactor((_m = this.props.lineStyle) === null || _m === void 0 ? void 0 : _m.dash), visible: this.props.outline && !fastDrawing, parameters: Object.assign(Object.assign({}, parameters), { [GL.POLYGON_OFFSET_FACTOR]: 1, [GL.POLYGON_OFFSET_UNITS]: 1 }) }));
const highlightLayerProps = this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { id: SubLayerId.HIGHLIGHT, data: getWellObjectByName(data.features, this.props.selectedWell), getLineWidth: getSize(LINE, (_o = this.props.lineStyle) === null || _o === void 0 ? void 0 : _o.width, 2), getPointRadius: getSize(POINT, (_p = this.props.wellHeadStyle) === null || _p === void 0 ? void 0 : _p.size, 2), getLineColor: getColor((_q = this.props.lineStyle) === null || _q === void 0 ? void 0 : _q.color), getFillColor: getColor((_r = this.props.wellHeadStyle) === null || _r === void 0 ? void 0 : _r.color), visible: this.props.logCurves && !fastDrawing }));
const highlightMultiWellsLayerProps = this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { id: SubLayerId.HIGHLIGHT_2, data: getWellObjectsByName(data.features, this.state.selectedMultiWells), getPointRadius: getSize(POINT, (_s = this.props.wellHeadStyle) === null || _s === void 0 ? void 0 : _s.size, 2), getFillColor: [255, 140, 0], getLineColor: [255, 140, 0], visible: this.props.logCurves && !fastDrawing }));
const fastLayer = new GeoJsonLayer(fastLayerProps);
const outlineLayer = new GeoJsonLayer(outlineLayerProps);
const colorsLayer = new GeoJsonLayer(colorsLayerProps);
// Highlight the selected well.
const highlightLayer = new GeoJsonLayer(highlightLayerProps);
// Highlight the multi selected wells.
const highlightMultiWellsLayer = new GeoJsonLayer(highlightMultiWellsLayerProps);
const logLayer = new PathLayer(this.getSubLayerProps({
id: SubLayerId.LOG_CURVE,
data: this.props.logData,
positionFormat,
pickable: true,
widthScale: 10,
widthMinPixels: 1,
miterLimit: 100,
getPath: (d) => {
var _a;
return getLogPath(data.features, d, this.props.logrunName, (_a = this.props.lineStyle) === null || _a === void 0 ? void 0 : _a.color);
},
getColor: (d) => getLogColor(d, this.props.logrunName, this.props.logName, this.props.logColor, this.context.userData
.colorTables, this.props.colorMappingFunction, this.props.isLog),
getWidth: (d) => this.props.logRadius ||
getLogWidth(d, this.props.logrunName, this.props.logName),
updateTriggers: {
getColor: [
this.props.logrunName,
this.props.logName,
this.props.logColor,
this.context.userData
.colorTables,
this.props.isLog,
],
getWidth: [
this.props.logrunName,
this.props.logName,
this.props.logRadius,
],
getPath: [positionFormat],
},
onDataLoad: (value) => {
this.setLegend(value);
},
parameters,
visible: this.props.logCurves && !fastDrawing,
}));
const selectionLayer = new PathLayer(this.getSubLayerProps({
id: SubLayerId.SELECTION,
data: this.props.logData,
positionFormat,
pickable: false,
widthScale: 10,
widthMinPixels: 1,
miterLimit: 100,
getPath: (d) => {
var _a;
return getLogPath1(data.features, d, this.state.well, this.state.selection, this.props.logrunName, (_a = this.props.lineStyle) === null || _a === void 0 ? void 0 : _a.color);
},
getColor: (d) => getLogColor1(data.features, d, this.state.well, this.state.selection, this.props.logrunName),
getWidth: (d) => this.props.logRadius * 1.5 ||
getLogWidth(d, this.props.logrunName, this.props.logName),
updateTriggers: {
getColor: [
this.props.logrunName,
this.state.well,
this.state.selection,
],
getWidth: [
this.props.logrunName,
this.props.logName,
this.props.logRadius,
],
getPath: [
positionFormat,
this.props.logrunName,
this.state.well,
this.state.selection,
],
},
onDataLoad: (value) => {
this.setLegend(value);
},
parameters,
visible: this.props.logCurves && !fastDrawing,
}));
const namesLayer = this.createWellLabelLayer(data.features);
const layers = [
outlineLayer,
logLayer,
colorsLayer,
highlightLayer,
highlightMultiWellsLayer,
selectionLayer,
namesLayer,
];
if (fastDrawing) {
layers.push(fastLayer);
}
return layers;
}
getPickingInfo({ info }) {
var _a, _b, _c, _d, _e, _f;
const noLog = {
properties: [],
logName: "",
};
if (!info.object || ((_a = info.sourceLayer) === null || _a === void 0 ? void 0 : _a.constructor) === WellLabelLayer) {
return Object.assign(Object.assign({}, info), noLog);
}
const features = (_c = (_b = this.getWellDataState()) === null || _b === void 0 ? void 0 : _b.features) !== null && _c !== void 0 ? _c : [];
const coordinate = info.coordinate || [0, 0, 0];
const zScale = this.props.modelMatrix ? this.props.modelMatrix[10] : 1;
if (typeof coordinate[2] !== "undefined") {
coordinate[2] /= Math.max(0.001, zScale);
}
let md_property = null;
let tvd_property = null;
let log_property = null;
// ! This needs to be updated if we ever change the sub-layer id!
if (((_d = info.sourceLayer) === null || _d === void 0 ? void 0 : _d.id) === `${this.props.id}-${SubLayerId.LOG_CURVE}`) {
// The user is hovering a well log entry
const logPick = info;
md_property = getLogProperty(coordinate, features, logPick.object, this.props.logrunName, "MD");
tvd_property = getLogProperty(coordinate, features, logPick.object, this.props.logrunName, "TVD");
log_property = getLogProperty(coordinate, features, info.object, this.props.logrunName, this.props.logName);
}
else {
// User is hovering a wellbore path
const wellpickInfo = info;
md_property = getMdProperty(coordinate, wellpickInfo.object, (_e = this.props.lineStyle) === null || _e === void 0 ? void 0 : _e.color, info.featureType);
tvd_property = getTvdProperty(coordinate, info.object, (_f = this.props.lineStyle) === null || _f === void 0 ? void 0 : _f.color, info.featureType);
}
// Patch for inverting tvd readout to fix issue #830,
// should make proper fix when handling z increase direction - issue #842
const inverted_tvd_property = tvd_property && Object.assign(Object.assign({}, tvd_property), { value: (tvd_property === null || tvd_property === void 0 ? void 0 : tvd_property.value) * -1 });
const layer_properties = [];
if (md_property)
layer_properties.push(md_property);
if (inverted_tvd_property)
layer_properties.push(inverted_tvd_property);
if (log_property)
layer_properties.push(log_property);
return Object.assign(Object.assign({}, info), { properties: layer_properties, logName: (log_property === null || log_property === void 0 ? void 0 : log_property.name) || "" });
}
}
WellsLayer.layerName = "WellsLayer";
WellsLayer.defaultProps = Object.assign(Object.assign({}, defaultProps), { onDataLoad: (data, context) => onDataLoad(data, context) });
//================= Local help functions. ==================
function onDataLoad(data, context) {
const bbox = GetBoundingBox(data);
if (typeof context.layer.props.reportBoundingBox !== "undefined") {
context.layer.props.reportBoundingBox({ layerBoundingBox: bbox });
}
}
function multiply(pair, factor) {
return [pair[0] * factor, pair[1] * factor];
}
function getDashFactor(accessor, width_accessor, offset = 0) {
return (object, objectInfo) => {
let width = DEFAULT_LINE_WIDTH;
if (typeof width_accessor == "function") {
width = width_accessor(object);
}
else if (width_accessor) {
width = width_accessor;
}
const factor = width / (width + offset);
let dash = [0, 0];
if (typeof accessor == "function") {
dash = accessor(object, objectInfo);
}
else if (accessor)
dash = accessor;
else if (accessor)
dash = DEFAULT_DASH;
if (dash.length == 2) {
return multiply(dash, factor);
}
else {
return multiply(DEFAULT_DASH, factor);
}
};
}
function dataIsReady(layerData) {
// Deck.gl always shows prop.data as `[]` while external data is being loaded
return !!layerData && !isEmpty(layerData);
}
function getColumn(data, col) {
const column = [];
for (let i = 0; i < data.length; i++) {
column.push(data[i][col]);
}
return column;
}
function getLogMd(d, logrun_name) {
if (!isSelectedLogRun(d, logrun_name))
return [];
const names_md = ["DEPTH", "DEPT", "MD", "TDEP", "MD_RKB"]; // aliases for MD
const log_id = getLogIndexByNames(d, names_md);
return log_id >= 0 ? getColumn(d.data, log_id) : [];
}
export function getLogValues(d, logrun_name, log_name) {
if (!isSelectedLogRun(d, logrun_name))
return [];
const log_id = getLogIndexByName(d, log_name);
return log_id >= 0 ? getColumn(d.data, log_id) : [];
}
export function getLogInfo(d, logrun_name, log_name) {
if (!isSelectedLogRun(d, logrun_name))
return undefined;
const log_id = getLogIndexByName(d, log_name);
return d.curves[log_id];
}
function getDiscreteLogMetadata(d, log_name) {
return d === null || d === void 0 ? void 0 : d.metadata_discrete[log_name];
}
function isSelectedLogRun(d, logrun_name) {
return d.header.name.toLowerCase() === logrun_name.toLowerCase();
}
function getWellObjectByName(wells_data, name) {
return wells_data === null || wells_data === void 0 ? void 0 : wells_data.find((item) => { var _a, _b; return ((_b = (_a = item.properties) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === (name === null || name === void 0 ? void 0 : name.toLowerCase()); });
}
function getWellObjectsByName(wells_data, name) {
const res = [];
for (let i = 0; i < (name === null || name === void 0 ? void 0 : name.length); i++) {
wells_data === null || wells_data === void 0 ? void 0 : wells_data.find((item) => {
var _a, _b, _c;
if (((_b = (_a = item.properties) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === ((_c = name[i]) === null || _c === void 0 ? void 0 : _c.toLowerCase()))
res.push(item);
});
}
return res;
}
function getPointGeometry(well_object) {
const geometries = well_object.geometry.geometries;
return geometries.find((item) => item.type === "Point");
}
// Return well head position from Point Geometry
function getWellHeadPosition(well_object) {
var _a, _b;
return (_b = (_a = getPointGeometry(well_object)) === null || _a === void 0 ? void 0 : _a.coordinates) !== null && _b !== void 0 ? _b : [-1, -1, -1];
}
function getWellMds(well_object) {
return well_object.properties.md[0];
}
function getNeighboringMdIndices(mds, md) {
const idx = mds.findIndex((x) => x >= md);
return idx === 0 ? [idx, idx + 1] : [idx - 1, idx];
}
function getPositionByMD(well_xyz, well_mds, md) {
const [l_idx, h_idx] = getNeighboringMdIndices(well_mds, md);
const md_low = well_mds[l_idx];
const md_normalized = (md - md_low) / (well_mds[h_idx] - md_low);
return interpolateNumberArray(well_xyz[l_idx], well_xyz[h_idx])(md_normalized);
}
function getLogPath(wells_data, d, logrun_name, trajectory_line_color) {
const well_object = getWellObjectByName(wells_data, d.header.well);
if (!well_object)
return [];
const well_xyz = getTrajectory(well_object, trajectory_line_color);
const well_mds = getWellMds(well_object);
if (well_xyz == undefined ||
well_mds == undefined ||
well_xyz.length == 0 ||
well_mds.length == 0)
return [];
const log_xyz = [];
const log_mds = getLogMd(d, logrun_name);
log_mds.forEach((md) => {
const xyz = getPositionByMD(well_xyz, well_mds, md);
log_xyz.push(xyz);
});
return log_xyz;
}
function getLogIndexByName(d, log_name) {
const name = log_name.toLowerCase();
return d.curves.findIndex((item) => item.name.toLowerCase() === name);
}
function getLogIndexByNames(d, names) {
for (const name of names) {
const index = getLogIndexByName(d, name);
if (index >= 0)
return index;
}
return -1;
}
function getLogColor(d, logrun_name, log_name, logColor, colorTables, colorMappingFunction, isLog) {
var _a;
const log_data = getLogValues(d, logrun_name, log_name);
const log_info = getLogInfo(d, logrun_name, log_name);
if (log_data.length == 0 || log_info == undefined)
return [];
const log_color = [];
if (log_info.description == "continuous") {
const min = Math.min(...log_data);
const max = Math.max(...log_data);
const max_delta = max - min;
log_data.forEach((value) => {
const adjustedVal = (value - min) / max_delta;
const rgb = colorMappingFunction
? colorMappingFunction(adjustedVal)
: rgbValues(adjustedVal, logColor, colorTables, isLog);
if (rgb) {
log_color.push([rgb[0], rgb[1], rgb[2]]);
}
else {
log_color.push([0, 0, 0, 0]); // push transparent for null/undefined log values
}
});
}
else {
// well log data set for ex : H1: Array(2)0: (4) [255, 26, 202, 255] 1: 13
const log_attributes = (_a = getDiscreteLogMetadata(d, log_name)) === null || _a === void 0 ? void 0 : _a.objects;
const logLength = Object.keys(log_attributes).length;
const attributesObject = {};
const categorical = true;
Object.keys(log_attributes).forEach((key) => {
// get the point from log_attributes
const point = log_attributes[key][1];
const categoricalMin = 0;
const categoricalMax = logLength - 1;
let rgb;
if (colorMappingFunction) {
rgb = colorMappingFunction(point, categorical, categoricalMin, categoricalMax);
}
else {
// if color-map function is not defined
const arrayOfColors = getColors(logColor, colorTables, point);
if (!arrayOfColors.length)
console.error(`Empty or missed '${logColor}' color table`);
else {
rgb = arrayOfColors;
}
}
if (rgb) {
if (rgb.length === 3) {
attributesObject[key] = [[rgb[0], rgb[1], rgb[2]], point];
}
else {
// ? What is the point of this? Why do we offset the index in this case, isn't the fourth value the opacity?
// (@anders2303)
attributesObject[key] = [[rgb[1], rgb[2], rgb[3]], point];
}
}
});
log_data.forEach((log_value) => {
var _a;
const dl_attrs = (_a = Object.entries(attributesObject).find(([, value]) => value[1] == log_value)) === null || _a === void 0 ? void 0 : _a[1];
if (dl_attrs)
log_color.push(dl_attrs[0]);
else
log_color.push([0, 0, 0, 0]); // use transparent for undefined/null log values
});
}
return log_color;
}
function getLogPath1(wells_data, d, selectedWell, selection, logrun_name, trajectory_line_color) {
if (!selection || selectedWell !== d.header.well)
return [];
const well_object = getWellObjectByName(wells_data, d.header.well);
if (!well_object)
return [];
const well_xyz = getTrajectory(well_object, trajectory_line_color);
const well_mds = getWellMds(well_object);
if (well_xyz == undefined ||
well_mds == undefined ||
well_xyz.length == 0 ||
well_mds.length == 0)
return [];
const log_mds = getLogMd(d, logrun_name);
if (!log_mds)
return [];
const log_xyz = [];
let md0 = selection[0];
if (md0 !== undefined) {
let md1 = selection[1];
if (md1 == md0)
md1 = undefined;
const mdFirst = well_mds[0];
const mdLast = well_mds[well_mds.length - 1];
if (md1 !== undefined) {
if (md0 > md1) {
const tmp = md0;
md0 = md1;
md1 = tmp;
}
}
const delta = 2;
if (md0 - delta > mdFirst) {
let xyz = getPositionByMD(well_xyz, well_mds, md0 - delta);
log_xyz.push(xyz);
xyz = getPositionByMD(well_xyz, well_mds, md0);
log_xyz.push(xyz);
}
if (md1 !== undefined) {
const _md1 = md1;
let index = 0;
well_mds.forEach((md) => {
if (md0 <= md && md <= _md1) {
const xyz = well_xyz[index];
log_xyz.push(xyz);
}
index++;
});
if (_md1 + delta < mdLast) {
let xyz = getPositionByMD(well_xyz, well_mds, _md1);
log_xyz.push(xyz);
xyz = getPositionByMD(well_xyz, well_mds, _md1 + delta);
log_xyz.push(xyz);
}
}
}
return log_xyz;
}
function getLogColor1(wells_data, d, selectedWell, selection, logrun_name) {
if (!selection || selectedWell !== d.header.well)
return [];
const well_object = getWellObjectByName(wells_data, d.header.well);
if (!well_object)
return [];
const well_mds = getWellMds(well_object);
const log_mds = getLogMd(d, logrun_name);
if (!log_mds || log_mds.length === 0)
return [];
const log_color = [];
let md0 = selection[0];
if (md0 !== undefined) {
const mdFirst = well_mds[0];
const mdLast = well_mds[well_mds.length - 1];
let md1 = selection[1];
if (md1 == md0)
md1 = undefined;
let swap = false;
if (md1 !== undefined) {
if (md0 > md1) {
const tmp = md0;
md0 = md1;
md1 = tmp;
swap = true;
}
}
const delta = 2;
if (md0 - delta > mdFirst)
log_color.push(swap ? [0, 255, 0, 128] : [255, 0, 0, 128]);
if (md1 !== undefined) {
const _md1 = md1;
log_color.push([128, 128, 128, 128]);
well_mds.forEach((md) => {
if (md0 <= md && md <= _md1) {
log_color.push([128, 128, 128, 128]);
}
});
if (_md1 + delta < mdLast)
log_color.push(swap ? [255, 0, 0, 128] : [0, 255, 0, 128]);
}
}
return log_color;
}
function getLogWidth(d, logrun_name, log_name) {
return getLogValues(d, logrun_name, log_name);
}
function squared_distance(a, b) {
const dx = a[0] - b[0];
const dy = a[1] - b[1];
return dx * dx + dy * dy;
}
// Return distance between line segment vw and point p
function distToSegmentSquared(v, w, p) {
const l2 = squared_distance(v, w);
if (l2 == 0)
return squared_distance(p, v);
let t = ((p[0] - v[0]) * (w[0] - v[0]) + (p[1] - v[1]) * (w[1] - v[1])) / l2;
t = Math.max(0, Math.min(1, t));
return squared_distance(p, [
v[0] + t * (w[0] - v[0]),
v[1] + t * (w[1] - v[1]),
]);
}
// Interpolates point closest to the coords on trajectory
function interpolateDataOnTrajectory(coord, data, trajectory) {
// if number of data points in less than 1 or
// length of data and trajectory are different we cannot interpolate.
if (data.length <= 1 || data.length != trajectory.length)
return -1;
// Identify closest well path leg to coord.
const segment_index = getSegmentIndex(coord, trajectory);
const index0 = segment_index;
const index1 = index0 + 1;
// Get the nearest data.
const data0 = data[index0];
const data1 = data[index1];
// Get the nearest survey points.
const survey0 = trajectory[index0];
const survey1 = trajectory[index1];
const dv = distance(survey0, survey1);
if (dv === 0) {
return -1;
}
// Calculate the scalar projection onto segment.
const v0 = subtract(coord, survey0);
const v1 = subtract(survey1, survey0);
// scalar_projection in interval [0,1]
const scalar_projection = dot(v0, v1) / (dv * dv);
// Interpolate data.
return data0 * (1.0 - scalar_projection) + data1 * scalar_projection;
}
function getMd(coord, feature, accessor) {
var _a, _b;
if (!((_b = (_a = feature.properties) === null || _a === void 0 ? void 0 : _a["md"]) === null || _b === void 0 ? void 0 : _b[0]) || !feature.geometry)
return null;
const measured_depths = feature.properties.md[0];
const trajectory3D = getTrajectory(feature, accessor);
if (trajectory3D == undefined)
return null;
let trajectory;
// In 2D view coord is of type Point2D and in 3D view it is Point3D,
// so use appropriate trajectory for interpolation
if (coord.length == 2) {
const trajectory2D = trajectory3D.map((v) => {
return v.slice(0, 2);
});
trajectory = trajectory2D;
}
else {
trajectory = trajectory3D;
}
return interpolateDataOnTrajectory(coord, measured_depths, trajectory);
}
function getMdProperty(coord, feature, accessor, featureType) {
var _a, _b;
if (featureType === "points") {
return null;
}
const md = getMd(coord, feature, accessor);
if (md != null) {
const prop_name = "MD " + ((_a = feature.properties) === null || _a === void 0 ? void 0 : _a["name"]);
return createPropertyData(prop_name, md, (_b = feature.properties) === null || _b === void 0 ? void 0 : _b["color"]);
}
return null;
}
function getTvd(coord, feature, accessor) {
var _a;
const trajectory3D = getTrajectory(feature, accessor);
// if trajectory is not found or if it has a data single point then get tvd from well head
if (trajectory3D == undefined || (trajectory3D === null || trajectory3D === void 0 ? void 0 : trajectory3D.length) <= 1) {
const wellhead_xyz = getWellHeadPosition(feature);
return (_a = wellhead_xyz === null || wellhead_xyz === void 0 ? void 0 : wellhead_xyz[2]) !== null && _a !== void 0 ? _a : null;
}
let trajectory;
// For 2D view coord is Point2D and for 3D view it is Point3D
if (coord.length == 2) {
const trajectory2D = trajectory3D === null || trajectory3D === void 0 ? void 0 : trajectory3D.map((v) => {
return v.slice(0, 2);
});
trajectory = trajectory2D;
}
else {
trajectory = trajectory3D;
}
const tvds = trajectory3D.map((v) => {
return v[2];
});
return interpolateDataOnTrajectory(coord, tvds, trajectory);
}
function getTvdProperty(coord, feature, accessor, featureType) {
var _a, _b;
if (featureType === "points") {
return null;
}
const tvd = getTvd(coord, feature, accessor);
if (tvd != null) {
const prop_name = "TVD " + ((_a = feature.properties) === null || _a === void 0 ? void 0 : _a["name"]);
return createPropertyData(prop_name, tvd, (_b = feature.properties) === null || _b === void 0 ? void 0 : _b["color"]);
}
return null;
}
// Identify closest path leg to coord.
function getSegmentIndex(coord, path) {
let min_d = Number.MAX_VALUE;
let segment_index = 0;
for (let i = 0; i < (path === null || path === void 0 ? void 0 : path.length) - 1; i++) {
const d = distToSegmentSquared(path[i], path[i + 1], coord);
if (d > min_d)
continue;
segment_index = i;
min_d = d;
}
return segment_index;
}
// Returns segment index of discrete logs
function getLogSegmentIndex(coord, wells_data, log_data, logrun_name) {
const trajectory = getLogPath(wells_data, log_data, logrun_name);
return getSegmentIndex(coord, trajectory);
}
function getLogProperty(coord, wells_data, log_data, logrun_name, log_name) {
var _a, _b, _c;
if (!log_data.data)
return null;
const segment_index = getLogSegmentIndex(coord, wells_data, log_data, logrun_name);
let log_value = getLogValues(log_data, logrun_name, log_name)[segment_index];
let dl_attrs = undefined;
const dl_metadata = (_a = getDiscreteLogMetadata(log_data, log_name)) === null || _a === void 0 ? void 0 : _a.objects;
if (dl_metadata) {
dl_attrs = Object.entries(dl_metadata).find(([, value]) => value[1] == log_value);
}
const log = (_b = getLogInfo(log_data, logrun_name, log_name)) === null || _b === void 0 ? void 0 : _b.name;
const prop_name = log + " " + log_data.header.well;
log_value = dl_attrs ? dl_attrs[0] + " (" + log_value + ")" : log_value;
if (log_value) {
const well_object = getWellObjectByName(wells_data, log_data.header.well);
return createPropertyData(prop_name, log_value, (_c = well_object === null || well_object === void 0 ? void 0 : well_object.properties) === null || _c === void 0 ? void 0 : _c["color"]);
}
else
return null;
}
// Return data required to build well layer legend
function getLegendData(logs, wellName, logName, logColor) {
if (!logs)
return null;
const log = wellName
? logs.find((log) => log.header.well == wellName)
: logs[0];
const logInfo = !log
? undefined
: getLogInfo(log, log.header.name, logName);
const title = "Wells / " + logName;
if (log && (logInfo === null || logInfo === void 0 ? void 0 : logInfo.description) == "discrete") {
const meta = log["metadata_discrete"];
const metadataDiscrete = meta[logName].objects;
return {
title: title,
colorName: logColor,
discrete: true,
metadata: metadataDiscrete,
};
}
else {
const minArray = [];
const maxArray = [];
logs.forEach(function (log) {
const logValues = getLogValues(log, log.header.name, logName);
minArray.push(Math.min(...logValues));
maxArray.push(Math.max(...logValues));
});
return {
title: title,
colorName: logColor,
discrete: false,
valueRange: [Math.min(...minArray), Math.max(...maxArray)],
};
}
}
//# sourceMappingURL=wellsLayer.js.map