Last updated

Seismic Map Image

This tutorial demonstrates how to create a basemap display using SeismicImage and AnnotatedWidget. The seismic image is a shape that represents a result of seismic data processing and is used as a part of SeismicViewWidget. In most cases it is enough to use widget directly, but sometimes it is necessary to create a more complex display like base map, where seismic can be located at some angle and position inside the display.

# Creating a Seismic Image

The first step is to create an image with empty pipeline. The data and pipeline will be set later. Instructions on how to initialize a pipeline and connect a reader can be found in Seismic Widget tutorial. The seismic image is created using geotoolkit/seismic/image/SeismicImage() and geotoolkit/seismic/data/MemoryReader(). The code below creates an empty image with a background and outlines. Here, the seismic image uses data limits from 0 to 1 in both directions as placeholders which are updated later to the real data limits.

# Creating a Seismic Layer

The next step is to add the created image to a group with axes and specify its location in the basemap. The code below shows the initialization of the group. The image and axes are added to this group with inner limits equal to seismic data limits (using the method setModelLimits) and outer limits in the basemap coordinates (using the method setBounds). The last line of code rotates the image and axes about 11 degrees (0.2 radians).

# Creating a Widget

The next step is to create a widget with a seismic layer. An AnnotatedWidget is used, which has a center part to display a seismic layer and other possible layers and a set of annotations on each side to display axes, titles, or a colorbar. The following code demonstrates this step of adding an image as a layer to the model and passing this model to a new instance of the widget.

# Set Seismic Data

The next step is to load seismic data and set it to the SeismicImage created before, using a reader and pipeline. Pass the created memory reader, which loads meta data and statistics and sets the real trace and samples count to image. This code also modifies axes limits to reflect seismic data limits.

# Result

The canvas below shows a result of the visualization.

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { SeismicImage } from "@int/geotoolkit/seismic/image/SeismicImage.ts";
import { AnchorType } from "@int/geotoolkit/util/AnchorType.ts";
import { Orientation } from "@int/geotoolkit/util/Orientation.ts";
import { LabelPosition, TickPosition } from "@int/geotoolkit/axis/TickInfo.ts";
import { Axis } from "@int/geotoolkit/axis/Axis.ts";
import { AnnotatedWidget } from "@int/geotoolkit/widgets/AnnotatedWidget.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { SeismicColors } from "@int/geotoolkit/seismic/util/SeismicColors.ts";
import { SeismicPipeline } from "@int/geotoolkit/seismic/pipeline/SeismicPipeline.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { MemoryReader } from "@int/geotoolkit/seismic/data/MemoryReader.ts";
import { MathUtil } from "@int/geotoolkit/util/MathUtil.ts";
class Plasma {
  constructor() {
  }
  getPoints(width, height, rough) {
    const p1 = MathUtil.getSeededRandom(0, 1, 16), p2 = MathUtil.getSeededRandom(0, 1, 16), p3 = MathUtil.getSeededRandom(0, 1, 16), p4 = MathUtil.getSeededRandom(0, 1, 16);
    const points = [];
    for (let x = 0; x < width; x++) {
      points[x] = [];
    }
    this.roughness = rough;
    this.totalSize = width + height;
    this.splitRect(points, 0, 0, width, height, p1, p2, p3, p4);
    return points;
  }
  splitRect(points, x, y, width, height, p1, p2, p3, p4) {
    let side1, side2, side3, side4, center;
    const transWidth = ~~(width / 2);
    const transHeight = ~~(height / 2);
    if (width > 1 || height > 1) {
      center = (p1 + p2 + p3 + p4) / 4;
      center += this.shift(transWidth + transHeight);
      side1 = (p1 + p2) / 2;
      side2 = (p2 + p3) / 2;
      side3 = (p3 + p4) / 2;
      side4 = (p4 + p1) / 2;
      center = this.normalize(center);
      side1 = this.normalize(side1);
      side2 = this.normalize(side2);
      side3 = this.normalize(side3);
      side4 = this.normalize(side4);
      this.splitRect(points, x, y, transWidth, transHeight, p1, side1, center, side4);
      this.splitRect(points, x + transWidth, y, width - transWidth, transHeight, side1, p2, side2, center);
      this.splitRect(points, x + transWidth, y + transHeight, width - transWidth, height - transHeight, center, side2, p3, side3);
      this.splitRect(points, x, y + transHeight, transWidth, height - transHeight, side4, center, side3, p4);
    } else {
      points[x][y] = (p1 + p2 + p3 + p4) / 4;
    }
  }
  normalize(val) {
    return val < 0 ? 0 : val > 1 ? 1 : val;
  }
  shift(smallSize) {
    return (MathUtil.getSeededRandom(0, 1, 16) - 0.5) * smallSize / this.totalSize * this.roughness;
  }
}
let seismicImage = null;
let xLineAxis = null;
let inLineAxis = null;
class PlasmaProcessor {
  constructor(tracesCount, samplesPerTrace) {
    this.plasma = new Plasma();
    this.points = this.plasma.getPoints(tracesCount, samplesPerTrace, 10);
    this.min = Number.POSITIVE_INFINITY;
    this.max = Number.NEGATIVE_INFINITY;
    this.average = 0;
    this.rms = 0;
    for (let x = 0; x < tracesCount; x++) {
      for (let y = 0; y < samplesPerTrace; y++) {
        const value = this.points[x][y];
        this.average += value;
        this.rms += value * value;
        this.min = Math.min(this.min, value);
        this.max = Math.max(this.max, value);
      }
    }
    this.average = this.average / (tracesCount * samplesPerTrace);
    this.rms = Math.sqrt(this.rms / (tracesCount * samplesPerTrace));
  }
  getDataStatistics(reader) {
    return {
      "average": this.average,
      "min": this.min,
      "max": this.max,
      "rms": this.rms
    };
  }
  getTraceData(reader, samples, traceId) {
    for (let i = 0; i < reader.getNumberOfSamples(); i++) {
      samples[i] = this.points[traceId][i];
    }
  }
}
function getPlasmaMemoryReader(tracesCount, samplesPerTrace, height) {
  return new MemoryReader({
    "numberoftraces": tracesCount,
    "numberofsamples": samplesPerTrace,
    "samplerate": height / samplesPerTrace
  }).setTraceProcessor(new PlasmaProcessor(tracesCount, samplesPerTrace));
}
function setSeismicData(seismicReader) {
  seismicReader.loadMetaData((reader) => {
    reader.readDataSetStatistics((reader2, statistics) => {
      const deviceBounds = seismicImage.getSceneTransform().transformRect(seismicImage.getBounds());
      const toTrace = Math.min(deviceBounds.getWidth(), reader2.getNumberOfTraces());
      const sampleRate = reader2.getSampleRate();
      const samplesAmount = reader2.getNumberOfSamples();
      const toSamples = Math.min(2 * deviceBounds.getHeight() * sampleRate, samplesAmount * sampleRate);
      xLineAxis.setModelLimits(xLineAxis.getModelLimits().clone().setHeight(toSamples - 1).setY(1));
      inLineAxis.setModelLimits(inLineAxis.getModelLimits().clone().setWidth(toTrace - 1).setX(1));
      seismicImage.setModelLimits(new Rect(0, 0, toTrace, toSamples)).setPipeline(new SeismicPipeline({
        "name": "seismic",
        "reader": reader2,
        "statistics": statistics
      }).setColorMap(SeismicColors.getDefault().createNamedColorMap("Saddleback", 32)).setOptions({
        "plot": {
          "type": {
            "wiggle": false,
            "interpolateddensity": true
          }
        },
        "colors": {
          "opacity": 0.5
        }
      }));
    });
  });
}
function createWidget(image, limits) {
  let axisSouth, axisWest;
  const model = new Group().addChild([
    image
  ]).setModelLimits(limits).setBounds(limits).setVerticalFlip(true);
  const widget = new AnnotatedWidget({
    "model": model,
    "annotationssizes": {
      "north": "5",
      "south": "60",
      "east": "12",
      "west": "60"
    },
    "north": [],
    "east": [],
    "south": [
      axisSouth = new Axis({
        "tickposition": TickPosition.Top,
        "orientation": Orientation.Horizontal,
        "title": {
          "text": "X Axis",
          "visible": true,
          "alignment": AnchorType.BottomCenter,
          "textstyle": {
            "color": "#757575"
          }
        }
      })
    ],
    "west": [
      axisWest = new Axis({
        "tickposition": TickPosition.Right,
        "labelposition": LabelPosition.Right,
        "orientation": Orientation.Vertical,
        "title": {
          "text": "Y Axis",
          "visible": true,
          "alignment": AnchorType.LeftCenter,
          "textstyle": {
            "color": "#757575"
          }
        }
      })
    ],
    "tools": {
      "horizontalscroll": {
        "visible": false
      },
      "verticalscroll": {
        "visible": false
      }
    }
  }).connect(axisWest, model).connect(axisSouth, model).refreshLayout();
  return widget;
}
function createImage() {
  return new SeismicImage(null, new Rect(0, 0, 1, 1));
}
function createMap(bounds) {
  const center = bounds.getCenter();
  const seismicBounds = bounds.clone().inflate(-70, -70);
  const depthAxisBounds = new Rect(seismicBounds.getX() - 50, seismicBounds.getTop(), seismicBounds.getX(), seismicBounds.getBottom());
  const tickAxisBounds = new Rect(seismicBounds.getLeft(), seismicBounds.getY() - 40, seismicBounds.getRight(), seismicBounds.getX());
  const group = new Group().setBounds(bounds.clone()).setModelLimits(bounds.clone()).addChild([
    new Group().setBounds(seismicBounds).setModelLimits(new Rect(0, 0, 1, 1)).addChild([
      seismicImage = createImage()
    ]),
    xLineAxis = new Axis().setBounds(depthAxisBounds).setModelLimits(new Rect(0, 0, 1, 100)).setBaseLineStyle(null).setTickPosition(TickPosition.Right),
    inLineAxis = new Axis().setBounds(tickAxisBounds).setModelLimits(new Rect(0, 0, 200, 1)).setBaseLineStyle(null).setTickPosition(TickPosition.Bottom).setOrientation(Orientation.Horizontal)
  ]).rotate(0.2, center.getX(), center.getY());
  return group;
}
function createScene(canvas) {
  const map = createMap(new Rect(0, 0, 500, 450));
  const widget = createWidget(map, new Rect(0, 0, 800, 450));
  setSeismicData(getPlasmaMemoryReader(149, 500, 80));
  const plot = new Plot({
    "canvaselement": canvas,
    "infiniteautoupdate": true,
    "root": widget
  });
  return plot;
}
export { createScene };

createScene(document.querySelector('[ref="plot"]'));