UNPKG

@equinor/esv-intersection

Version:

Intersection component package with testing and automatic documentation.

206 lines (178 loc) 5.37 kB
import { clamp } from '@equinor/videx-math'; import { SeismicCanvasDataOptions } from '../layers/SeismicCanvasLayer'; import { createColorTable } from './colortable'; import { findIndexOfSample } from './findsample'; export type SeismicInfo = { minX: number; maxX: number; minTvdMsl: number; maxTvdMsl: number; domain: { min: number; max: number; difference: number; }; }; export const getSeismicOptions = ( info: SeismicInfo | null, ): SeismicCanvasDataOptions => { if (!info) { return { x: 0, y: 0, width: 0, height: 0, }; } return { x: info.minX, y: info.minTvdMsl, width: info.maxX - info.minX, height: info.maxTvdMsl - info.minTvdMsl, }; }; /** * Get key information about the seismic data * Code originally developed for the REP project * @param data Seismic data * @param trajectory Wellbore or freehand trajectory * @return Key domain and depth information for seismic data */ export function getSeismicInfo( data: { datapoints: number[][]; yAxisValues: number[] }, trajectory: number[][], ): SeismicInfo | null { if (!(data && data.datapoints)) { return null; } const minX = trajectory.reduce( (acc: number, val: number[]) => Math.min(acc, val[0]!), 0, ); const maxX = trajectory.reduce( (acc: number, val: number[]) => Math.max(acc, val[0]!), 0, ); const minTvdMsl = data.yAxisValues && data.yAxisValues[0]!; const maxTvdMsl = data.yAxisValues && data.yAxisValues[data.yAxisValues.length - 1]!; // Find value domain const dp = data.datapoints || []; const min = -dp.reduce( (val: number, array: number[]) => Math.min(...array, val), 0, ); const max = dp.reduce( (val: number, array: number[]) => Math.max(...array, val), 0, ); const absMax = Math.max(Math.abs(min), Math.abs(max)); const dmin = -absMax; const dmax = absMax; const info = { minX, maxX, minTvdMsl, maxTvdMsl, domain: { min: dmin, max: dmax, difference: dmax - dmin, }, }; return info; } /** * Generate seismic * Code originally developed for the REP project * @param data Seismic data * @param trajectory Wellbore or freehand trajectory * @param colormap Color map for rendering * @param options.isLeftToRight (optional) draw left to right * @param options.seismicRange (optional) Range for mapping seimic values to color map * @param options.seismicMin (optional) Min seismic value for mapping seimic values to color map * @param options.seismicMax (optional) Max seismic value for mapping seimic values to color map * @return Key domain and depth information for seismic data */ export async function generateSeismicSliceImage( data: { datapoints: number[][]; yAxisValues: number[] }, trajectory: number[][], colormap: string[], options: { isLeftToRight: boolean; seismicRange?: number; seismicMin?: number; seismicMax?: number; } = { isLeftToRight: true }, ): Promise<ImageBitmap | undefined> { if (!(data && data.datapoints && data.datapoints.length > 0)) { return undefined; } const { datapoints: dp } = data; const min = options?.seismicMin || options?.seismicRange || dp.reduce((val: number, array: number[]) => Math.min(...array, val), 0); const max = options?.seismicMax || options?.seismicRange || dp.reduce((val: number, array: number[]) => Math.max(...array, val), 0); const absMax = Math.max(Math.abs(min), Math.abs(max)); const dmin = -absMax; const dmax = absMax; const domain = { min: dmin, max: dmax, difference: dmax - dmin, }; const length = trajectory[0]?.[0]! - trajectory[trajectory.length - 1]?.[0]!; const width = Math.abs(Math.floor(length / 5)); const height = data.yAxisValues.length; // Generate color table const colorTableSize = 1000; const colorTable = createColorTable(colormap, colorTableSize); // Generate image const d = new Uint8ClampedArray(width * height * 4); let offset = 0; const colorFactor = (colorTableSize - 1) / domain.difference; let pos = options?.isLeftToRight ? trajectory[0]?.[0]! : trajectory[trajectory.length - 1]?.[0]!; const step = (length / width) * (options?.isLeftToRight ? -1 : 1); let val1: number; let val2: number; let val: number; let i: number; let col: number[]; const black = [0, 0, 0]; let opacity: number; for (let x = 0; x < width; x++) { offset = x * 4; const index = findIndexOfSample(trajectory, pos); const x1 = trajectory[index]?.[0]!; const x2 = trajectory[index + 1]?.[0]!; const span = x2 - x1; const dx = pos - x1; const ratio = dx / span; for (let y = 0; y < height; y++) { val1 = dp[y]?.[index]!; val2 = dp[y]?.[index + 1]!; if (val1 == null || val2 == null) { col = black; opacity = 0; } else { val = val1 * (1 - ratio) + val2 * ratio; i = (val - domain.min) * colorFactor; i = clamp(~~i, 0, colorTableSize - 1); col = colorTable[i]!; opacity = 255; } d.set([col[0]!, col[1]!, col[2]!, opacity], offset); offset += width * 4; } pos += step; } const imageData = new ImageData(d, width, height); const image = await createImageBitmap(imageData, 0, 0, width, height); return image; }