UNPKG

@webviz/subsurface-viewer

Version:

3D visualization component for subsurface reservoir data

332 lines 14.4 kB
import { cloneDeep } from "lodash"; import { simplify } from "../../utils/simplify"; export const DEFAULT_TOLERANCE = 0.01; export function removeConsecutiveDuplicates(coords, mds) { // Filter out consecutive duplicate vertices. const keep = coords.map((e, index, arr) => { if (index < arr.length - 1) { return (e[0] !== arr[index + 1][0] || e[1] !== arr[index + 1][1] || e[2] !== arr[index + 1][2]); } return true; }); coords = coords.filter((_e, index) => { return keep[index]; }); mds = mds.filter((_e, index) => { return keep[index]; }); return [coords, mds]; } // Remove duplicates in well string // If z value of well head not defined set it to top of well string. export function checkWells(data) { var _a, _b; const no_wells = data.features.length; for (let well_no = 0; well_no < no_wells; well_no++) { const geometryCollection = data.features[well_no] .geometry; const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1]; if (((_a = lineString.coordinates) === null || _a === void 0 ? void 0 : _a.length) === undefined) { continue; } let coords = lineString.coordinates; // If not defined set wellhead z value to top of well string. const wellHead = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[0]; if (wellHead.coordinates && wellHead.coordinates.length === 2) { wellHead.coordinates.push(coords[0][2]); } const mds = (_b = data.features[well_no].properties) === null || _b === void 0 ? void 0 : _b["md"]; if (mds === undefined) { continue; } const nOrig = coords.length; [coords, mds[0]] = removeConsecutiveDuplicates(coords, mds[0]); const n = coords.length; if (n != nOrig) { console.warn("Well number ", well_no, " contains duplicates."); } if (n <= 1) { continue; } data.features[well_no].geometry .geometries[1].coordinates = coords; if (data.features[well_no].properties) { data.features[well_no].properties["md"] = mds; // eslint-disable-line } } } /** * Given four points P0, P1, P2, P4 and a argument t in the interval [0,1]. * returns function value at t. t == 0 corresponds to P1 and t == 1 corrsponds to P2 * * See https://qroph.github.io/2018/07/30/smooth-paths-using-catmull-rom-splines.html */ export function CatmullRom1D(P0, P1, P2, P3, t) { const alpha = 0.5; const tt = t * t; const ttt = t * t * t; const dist_p0_p1 = Math.sqrt((P1 - P0) * (P1 - P0) + (P1 - P0) * (P1 - P0) + (P1 - P0) * (P1 - P0)); const dist_p1_p2 = Math.sqrt((P1 - P2) * (P1 - P2) + (P1 - P2) * (P1 - P2) + (P1 - P2) * (P1 - P2)); const dist_p2_p3 = Math.sqrt((P3 - P2) * (P3 - P2) + (P3 - P2) * (P3 - P2) + (P3 - P2) * (P3 - P2)); const t01 = Math.pow(dist_p0_p1, alpha); const t12 = Math.pow(dist_p1_p2, alpha); const t23 = Math.pow(dist_p2_p3, alpha); const m1 = P2 - P1 + t12 * ((P1 - P0) / t01 - (P2 - P0) / (t01 + t12)); const m2 = P2 - P1 + t12 * ((P3 - P2) / t23 - (P3 - P1) / (t12 + t23)); const a_x = 2 * (P1 - P2) + m1 + m2; const b_x = -3 * (P1 - P2) - m1 - m1 - m2; const c_x = m1; const d_x = P1; const x = a_x * ttt + b_x * tt + c_x * t + d_x; return x; } /** * Given four 3D points P0, P1, P2, P4 and a scalar argument t in the interval [0,1]. * returns function value (3D) at t. t == 0 corresponds to P1 and t == 1 corrsponds to P2 * * See https://qroph.github.io/2018/07/30/smooth-paths-using-catmull-rom-splines.html */ // prettier-ignore export function CatmullRom(P0, P1, P2, P3, t) { const alpha = 0.5; const tt = t * t; const ttt = t * t * t; // disable eslint for some lines due to readability. const dist_p0_p1 = Math.sqrt((P1[0] - P0[0]) * (P1[0] - P0[0]) + (P1[1] - P0[1]) * (P1[1] - P0[1]) + (P1[2] - P0[2]) * (P1[2] - P0[2])); // eslint-disable-line const dist_p1_p2 = Math.sqrt((P1[0] - P2[0]) * (P1[0] - P2[0]) + (P1[1] - P2[1]) * (P1[1] - P2[1]) + (P1[2] - P2[2]) * (P1[2] - P2[2])); // eslint-disable-line const dist_p2_p3 = Math.sqrt((P3[0] - P2[0]) * (P3[0] - P2[0]) + (P3[1] - P2[1]) * (P3[1] - P2[1]) + (P3[2] - P2[2]) * (P3[2] - P2[2])); // eslint-disable-line const t01 = Math.pow(dist_p0_p1, alpha); const t12 = Math.pow(dist_p1_p2, alpha); const t23 = Math.pow(dist_p2_p3, alpha); const m1_x = (P2[0] - P1[0] + t12 * ((P1[0] - P0[0]) / t01 - (P2[0] - P0[0]) / (t01 + t12))); // eslint-disable-line const m1_y = (P2[1] - P1[1] + t12 * ((P1[1] - P0[1]) / t01 - (P2[1] - P0[1]) / (t01 + t12))); // eslint-disable-line const m1_z = (P2[2] - P1[2] + t12 * ((P1[2] - P0[2]) / t01 - (P2[2] - P0[2]) / (t01 + t12))); // eslint-disable-line const m2_x = (P2[0] - P1[0] + t12 * ((P3[0] - P2[0]) / t23 - (P3[0] - P1[0]) / (t12 + t23))); // eslint-disable-line const m2_y = (P2[1] - P1[1] + t12 * ((P3[1] - P2[1]) / t23 - (P3[1] - P1[1]) / (t12 + t23))); // eslint-disable-line const m2_z = (P2[2] - P1[2] + t12 * ((P3[2] - P2[2]) / t23 - (P3[2] - P1[2]) / (t12 + t23))); // eslint-disable-line const a_x = 2 * (P1[0] - P2[0]) + m1_x + m2_x; const a_y = 2 * (P1[1] - P2[1]) + m1_y + m2_y; const a_z = 2 * (P1[2] - P2[2]) + m1_z + m2_z; const b_x = -3 * (P1[0] - P2[0]) - m1_x - m1_x - m2_x; const b_y = -3 * (P1[1] - P2[1]) - m1_y - m1_y - m2_y; const b_z = -3 * (P1[2] - P2[2]) - m1_z - m1_z - m2_z; const c_x = m1_x; const c_y = m1_y; const c_z = m1_z; const d_x = P1[0]; const d_y = P1[1]; const d_z = P1[2]; const x = a_x * ttt + b_x * tt + c_x * t + d_x; const y = a_y * ttt + b_y * tt + c_y * t + d_y; const z = a_z * ttt + b_z * tt + c_z * t + d_z; return [x, y, z]; } /** * Will interpolate and refine wellpaths using spline interploation resulting * in smoother curves with more points. * Assumes 3D data. */ export function splineRefine(data_in, stepCount = 5) { var _a, _b; if (stepCount < 1) { return data_in; } const data = cloneDeep(data_in); const no_wells = data.features.length; const step = 1 / stepCount; const steps = Array(stepCount - 1) .fill(0) .map((_x, index) => (index + 1) * step); for (let well_no = 0; well_no < no_wells; well_no++) { const mds = (_a = data.features[well_no].properties) === null || _a === void 0 ? void 0 : _a["md"]; if (mds === undefined) { continue; } const geometryCollection = data.features[well_no].geometry; const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1]; if (((_b = lineString.coordinates) === null || _b === void 0 ? void 0 : _b.length) === undefined) { continue; } const coords = lineString.coordinates; const n = coords.length; if (n <= 1) { continue; } const ts = n > 3 ? steps : []; // Point before first. const x0 = coords[0][0] - coords[1][0] + coords[0][0]; const y0 = coords[0][1] - coords[1][1] + coords[0][1]; const z0 = coords[0][2] - coords[1][2] + coords[0][2]; const P_first = [x0, y0, z0]; const md_first = mds[0][0] - mds[0][1] + mds[0][0]; // Point after last. const xn = coords[n - 1][0] - coords[n - 2][0] + coords[n - 1][0]; const yn = coords[n - 1][1] - coords[n - 2][1] + coords[n - 1][1]; const zn = coords[n - 1][2] - coords[n - 2][2] + coords[n - 1][2]; const P_n = [xn, yn, zn]; const md_n = mds[0][n - 1] - mds[0][n - 2] + mds[0][n - 1]; const newCoordinates = []; const newMds = []; newMds.push([]); for (let i = 0; i < n - 1; i += 1) { let P0, P1, P2, P3; let md0, md1, md2, md3; if (i === 0) { P0 = P_first; P1 = coords[i + 0]; P2 = coords[i + 1]; P3 = coords[i + 2]; md0 = md_first; md1 = mds[0][i + 0]; md2 = mds[0][i + 1]; md3 = mds[0][i + 2]; } else if (i === n - 2) { P0 = coords[n - 3]; P1 = coords[n - 2]; P2 = coords[n - 1]; P3 = P_n; md0 = mds[0][n - 3]; md1 = mds[0][n - 2]; md2 = mds[0][n - 1]; md3 = md_n; } else { P0 = coords[i - 1]; P1 = coords[i - 0]; P2 = coords[i + 1]; P3 = coords[i + 2]; md0 = mds[0][i - 1]; md1 = mds[0][i - 0]; md2 = mds[0][i + 1]; md3 = mds[0][i + 2]; } newCoordinates.push(P1); newMds[0].push(md1); // Skip first leg from platform to first survey point. if (i > 1) { for (let t_i = 0; t_i < ts.length; t_i += 1) { const t = ts[t_i]; const [x, y, z] = CatmullRom(P0, P1, P2, P3, t); const md = CatmullRom1D(md0, md1, md2, md3, t); newCoordinates.push([x, y, z]); newMds[0].push(md); } } } newCoordinates.push(coords[n - 1]); newMds[0].push(mds[0][n - 1]); data.features[well_no].geometry.geometries[1].coordinates = newCoordinates; if (data.features[well_no].properties) { data.features[well_no].properties["md"] = newMds; // eslint-disable-line } } return data; } /** * Will reduce/coarse the wellpaths. */ export function coarsenWells(dataIn, tolerance = DEFAULT_TOLERANCE) { var _a, _b; const data = cloneDeep(dataIn); const wellCount = data.features.length; for (let wellIndex = 0; wellIndex < wellCount; wellIndex++) { const geometryCollection = data.features[wellIndex] .geometry; const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1]; if (((_a = lineString.coordinates) === null || _a === void 0 ? void 0 : _a.length) === undefined) { continue; } const properties = data.features[wellIndex] .properties; if (properties) { const mds = (_b = properties["md"]) === null || _b === void 0 ? void 0 : _b[0]; const [newPoints, newMds] = simplify(lineString.coordinates, mds !== null && mds !== void 0 ? mds : [], tolerance); lineString.coordinates = newPoints; if (properties["md"]) { properties["md"][0] = newMds; } } } return data; } export function flattenPath(data_in) { var _a; const data = cloneDeep(data_in); const no_wells = data.features.length; for (let well_no = 0; well_no < no_wells; well_no++) { const geometryCollection = data.features[well_no] .geometry; const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1]; if (((_a = lineString.coordinates) === null || _a === void 0 ? void 0 : _a.length) === undefined) { continue; } const coords = lineString.coordinates; // flatten by setting z value constant. const coords_flat = coords.map((e) => { return [e[0], e[1], 0.0]; }); data.features[well_no].geometry .geometries[1].coordinates = coords_flat; } return data; } export function invertPath(data_in) { var _a, _b; const data = cloneDeep(data_in); const no_wells = data.features.length; for (let well_no = 0; well_no < no_wells; well_no++) { const geometryCollection = data.features[well_no].geometry; const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1]; const wellHead = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[0]; if ((_a = wellHead.coordinates) === null || _a === void 0 ? void 0 : _a[2]) { wellHead.coordinates[2] *= -1; } if (((_b = lineString.coordinates) === null || _b === void 0 ? void 0 : _b.length) === undefined) { continue; } const coords = lineString.coordinates; // Invert path by multiplying depth with -1. const coords_inverted = coords.map((e) => { return [e[0], e[1], -e[2]]; }); data.features[well_no].geometry.geometries[1].coordinates = coords_inverted; } return data; } /** * Calculates bounding box of all wells. */ export function GetBoundingBox(data) { var _a; let xMin = 9999999999; let yMin = 9999999999; let zMin = 9999999999; let xMax = -9999999999; let yMax = -9999999999; let zMax = -9999999999; const no_wells = data.features.length; for (let well_no = 0; well_no < no_wells; well_no++) { const geometryCollection = data.features[well_no] .geometry; const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1]; if (((_a = lineString.coordinates) === null || _a === void 0 ? void 0 : _a.length) === undefined) { continue; } const coords = lineString.coordinates; const n = coords.length; for (let i = 0; i < n; i++) { const xyz = coords[i]; xMin = xyz[0] < xMin ? xyz[0] : xMin; yMin = xyz[1] < yMin ? xyz[1] : yMin; zMin = xyz[2] < zMin ? xyz[2] : zMin; xMax = xyz[0] > xMax ? xyz[0] : xMax; yMax = xyz[1] > yMax ? xyz[1] : yMax; zMax = xyz[2] > zMax ? xyz[2] : zMax; } } return [xMin, yMin, zMin, xMax, yMax, zMax]; } //# sourceMappingURL=spline.js.map