ElevationReader

Introduction

The vtkElevationReader aims to read a text file formatted as below and create
a bumpy plane based on the elevation defined in that file. If a line has 10
elevation values, that means the plane will have 10 points along the X axis.
If the file has 5 lines, that means the plane will have 5 points along the Y
axis.

1 2 3 4 5
5 4 3 2 1
1 2 3 4 5
5 4 3 2 1
1 2 3 4 5

Each number represents an elevation on a uniform grid where a line
(horizontal) define the elevations along the X axis. With that in mind, new
lines (vertical) define the elevations along the Y axis and the actual number
is the elevation along Z.

In order to properly represent that in world coordinates, you can provide an
origin which will define the coordinate of the first point without its
elevation. Then you need to describe how much you should move along X and Y
between two elevations definition. For that we use xSpacing and ySpacing.
Since the elevation is given to us as a number, we can scale it via
zScaling. Finally you may decide that your grid should move along positive
X and negative Y while reading the file. The xDirection and yDirection
are meant to give you control on that end.

Methods

extend

Method used to decorate a given object (publicAPI+model) with vtkElevationReader characteristics.

Argument Type Required Description
publicAPI Yes object on which methods will be bounds (public)
model Yes object on which data structure will be bounds (protected)
initialValues IElevationReaderInitialValues No (default: {})

getDataAccessHelper

getUrl

Get the url of the object to load.

getXDirection

getXSpacing

getYDirection

getYSpacing

getZScaling

invokeBusy

Argument Type Required Description
busy Boolean Yes

isBusy

loadData

Load the object data.

Argument Type Required Description
options IElevationReaderOptions No

newInstance

Method used to create a new instance of vtkElevationReader

Argument Type Required Description
initialValues IElevationReaderInitialValues No for pre-setting some of its content

onBusy

Argument Type Required Description
callback Yes

parseAsText

Parse data as text.

Argument Type Required Description
content String Yes The content to parse.

requestData

Argument Type Required Description
inData Yes
outData Yes

setDataAccessHelper

Argument Type Required Description
dataAccessHelper Yes

setUrl

Set the url of the object to load.

Argument Type Required Description
url String Yes the url of the object to load.
option IElevationReaderOptions No The Elevation reader options.

setXDirection

Argument Type Required Description
xDirection Number Yes

setXSpacing

Argument Type Required Description
xSpacing Number Yes

setYDirection

Argument Type Required Description
yDirection Number Yes

setYSpacing

Argument Type Required Description
ySpacing Number Yes

setZScaling

Argument Type Required Description
zScaling Number Yes

Source

index.d.ts
import { vtkAlgorithm, vtkObject, vtkSubscription } from "../../../interfaces";
import HtmlDataAccessHelper from "../../Core/DataAccessHelper/HtmlDataAccessHelper";
import HttpDataAccessHelper from "../../Core/DataAccessHelper/HttpDataAccessHelper";
import JSZipDataAccessHelper from "../../Core/DataAccessHelper/JSZipDataAccessHelper";
import LiteHttpDataAccessHelper from "../../Core/DataAccessHelper/LiteHttpDataAccessHelper";


interface IElevationReaderOptions {
binary?: boolean;
compression?: string;
progressCallback?: any;
}

/**
*
*/
export interface IElevationReaderInitialValues {
origin?: number[];
xSpacing?: number;
ySpacing?: number;
zScaling?: number;
xDirection?: number;
yDirection?: number;
requestCount?: number;
}

type vtkElevationReaderBase = vtkObject & Omit<vtkAlgorithm,
| 'getInputData'
| 'setInputData'
| 'setInputConnection'
| 'getInputConnection'
| 'addInputConnection'
| 'addInputData'>;

export interface vtkElevationReader extends vtkElevationReaderBase {

/**
*
*/
getDataAccessHelper(): HtmlDataAccessHelper | HttpDataAccessHelper | JSZipDataAccessHelper | LiteHttpDataAccessHelper;

/**
* Get the url of the object to load.
*/
getUrl(): string;

/**
*
*/
getXDirection(): number;

/**
*
*/
getXSpacing(): number;

/**
*
*/
getYDirection(): number;

/**
*
*/
getYSpacing(): number;

/**
*
*/
getZScaling(): number;

/**
*
* @param {Boolean} busy
*/
invokeBusy(busy: boolean): void;

/**
*
*/
isBusy(): number;

/**
* Load the object data.
* @param {IElevationReaderOptions} [options]
*/
loadData(options?: IElevationReaderOptions): Promise<any>;

/**
*
* @param callback
*/
onBusy(callback: (busy: boolean) => any): vtkSubscription;

/**
* Parse data as text.
* @param {String} content The content to parse.
*/
parseAsText(content: string): void;

/**
*
* @param inData
* @param outData
*/
requestData(inData: any, outData: any): void;

/**
*
* @param dataAccessHelper
*/
setDataAccessHelper(dataAccessHelper: HtmlDataAccessHelper | HttpDataAccessHelper | JSZipDataAccessHelper | LiteHttpDataAccessHelper): boolean;

/**
* Set the url of the object to load.
* @param {String} url the url of the object to load.
* @param {IElevationReaderOptions} [option] The Elevation reader options.
*/
setUrl(url: string, option?: IElevationReaderOptions): Promise<any>;

/**
*
* @param {Number} xDirection
*/
setXDirection(xDirection: number): boolean;

/**
*
* @param {Number} xSpacing
*/
setXSpacing(xSpacing: number): boolean;

/**
*
* @param {Number} yDirection
*/
setYDirection(yDirection: number): boolean;

/**
*
* @param {Number} ySpacing
*/
setYSpacing(ySpacing: number): boolean;

/**
*
* @param {Number} zScaling
*/
setZScaling(zScaling: number): boolean;
}

/**
* Method used to decorate a given object (publicAPI+model) with vtkElevationReader characteristics.
*
* @param publicAPI object on which methods will be bounds (public)
* @param model object on which data structure will be bounds (protected)
* @param {IElevationReaderInitialValues} [initialValues] (default: {})
*/
export function extend(publicAPI: object, model: object, initialValues?: IElevationReaderInitialValues): void;

/**
* Method used to create a new instance of vtkElevationReader
* @param {IElevationReaderInitialValues} [initialValues] for pre-setting some of its content
*/
export function newInstance(initialValues?: IElevationReaderInitialValues): vtkElevationReader;


/**
* The vtkElevationReader aims to read a text file formatted as below and create
* a bumpy plane based on the elevation defined in that file. If a line has 10
* elevation values, that means the plane will have 10 points along the X axis.
* If the file has 5 lines, that means the plane will have 5 points along the Y
* axis.
*
* ```
* 1 2 3 4 5
* 5 4 3 2 1
* 1 2 3 4 5
* 5 4 3 2 1
* 1 2 3 4 5
* ```
*
* Each number represents an elevation on a uniform grid where a line
* (horizontal) define the elevations along the X axis. With that in mind, new
* lines (vertical) define the elevations along the Y axis and the actual number
* is the elevation along Z.
*
* In order to properly represent that in world coordinates, you can provide an
* `origin` which will define the coordinate of the first point without its
* elevation. Then you need to describe how much you should move along X and Y
* between two elevations definition. For that we use `xSpacing` and `ySpacing`.
* Since the elevation is given to us as a number, we can scale it via
* `zScaling`. Finally you may decide that your grid should move along positive
* X and negative Y while reading the file. The `xDirection` and `yDirection`
* are meant to give you control on that end.
*/
export declare const vtkElevationReader: {
newInstance: typeof newInstance;
extend: typeof extend;
}
export default vtkElevationReader;
index.js
import macro from 'vtk.js/Sources/macros';
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';
import vtkCellArray from 'vtk.js/Sources/Common/Core/CellArray';
import DataAccessHelper from 'vtk.js/Sources/IO/Core/DataAccessHelper';

// Enable several sources for DataAccessHelper
import 'vtk.js/Sources/IO/Core/DataAccessHelper/LiteHttpDataAccessHelper'; // Just need HTTP
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper'; // HTTP + gz
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HtmlDataAccessHelper'; // html + base64 + zip
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/JSZipDataAccessHelper'; // zip

// ----------------------------------------------------------------------------
// vtkElevationReader methods
// ----------------------------------------------------------------------------

function vtkElevationReader(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkElevationReader');

// Create default dataAccessHelper if not available
if (!model.dataAccessHelper) {
model.dataAccessHelper = DataAccessHelper.get('http');
}

// Internal method to fetch Array
function fetchCSV(url, options) {
return model.dataAccessHelper.fetchText(publicAPI, url, options);
}

// Set DataSet url
publicAPI.setUrl = (url, options) => {
model.url = url;

// Fetch metadata
return publicAPI.loadData(options);
};

// Fetch the actual data arrays
publicAPI.loadData = (options) =>
fetchCSV(model.url, options).then((csv) => {
publicAPI.parseAsText(csv);
return true;
});

publicAPI.parseAsText = (csv) => {
model.csv = csv;
model.elevation = [];

// Parse data
const lines = model.csv.split('\n');
lines.forEach((line, lineIdx) => {
model.elevation.push(line.split(',').map((str) => Number(str)));
});
publicAPI.modified();
};

publicAPI.requestData = (inData, outData) => {
const polydata = vtkPolyData.newInstance();
polydata.getPoints().setData(new Float32Array(0, 0, 0, 1, 1, 1), 3);

if (model.elevation) {
const jSize = model.elevation.length;
const iSize = model.elevation[0].length;

// Handle points and polys
const points = polydata.getPoints();
points.setNumberOfPoints(iSize * jSize, 3);
const pointValues = points.getData();

const polys = vtkCellArray.newInstance({
size: 5 * (iSize - 1) * (jSize - 1),
});
polydata.setPolys(polys);
const polysValues = polys.getData();
let cellOffset = 0;

// Texture coords
const tcData = new Float32Array(iSize * jSize * 2);
const tcoords = vtkDataArray.newInstance({
numberOfComponents: 2,
values: tcData,
name: 'TextureCoordinates',
});
polydata.getPointData().setTCoords(tcoords);

for (let j = 0; j < jSize; j++) {
for (let i = 0; i < iSize; i++) {
const offsetIdx = j * iSize + i;
const offsetPt = 3 * offsetIdx;

// Fill points coordinates
pointValues[offsetPt + 0] =
model.origin[0] + i * model.xSpacing * model.xDirection;
pointValues[offsetPt + 1] =
model.origin[1] + j * model.ySpacing * model.yDirection;
pointValues[offsetPt + 2] =
model.origin[2] + model.elevation[j][i] * model.zScaling;

// fill in tcoords
tcData[offsetIdx * 2] = i / (iSize - 1.0);
tcData[offsetIdx * 2 + 1] = 1.0 - j / (jSize - 1.0);

// Fill polys
if (i > 0 && j > 0) {
polysValues[cellOffset++] = 4;
polysValues[cellOffset++] = offsetIdx;
polysValues[cellOffset++] = offsetIdx - 1;
polysValues[cellOffset++] = offsetIdx - 1 - iSize;
polysValues[cellOffset++] = offsetIdx - iSize;
}
}
}
}

model.output[0] = polydata;
};

// return Busy state
publicAPI.isBusy = () => !!model.requestCount;
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
origin: [0, 0, 0],
xSpacing: 1,
ySpacing: 1,
zScaling: 1,
xDirection: 1,
yDirection: -1,
requestCount: 0,
// dataAccessHelper: null,
// url: null,
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);

// Build VTK API
macro.obj(publicAPI, model);
macro.get(publicAPI, model, ['url']);
macro.setGet(publicAPI, model, [
'dataAccessHelper',
'xSpacing',
'ySpacing',
'zScaling',
'xDirection',
'yDirection',
]);
macro.algo(publicAPI, model, 0, 1);
macro.event(publicAPI, model, 'busy');

// Object methods
vtkElevationReader(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(extend, 'vtkElevationReader');

// ----------------------------------------------------------------------------

export default { newInstance, extend };