项目作者: DanielJDufour

项目描述 :
Multi-Dimensional Functions. Create, Query, and Transform Multi-Dimensional Data.
高级语言: JavaScript
项目地址: git://github.com/DanielJDufour/xdim.git
创建时间: 2021-07-28T19:18:32Z
项目社区:https://github.com/DanielJDufour/xdim

开源协议:Creative Commons Zero v1.0 Universal

下载


xdim

Multi-Dimensional Functions

motivation

I work a lot with satellite imagery. In theory, most satellite imagery has three dimensions: (1) band, (2) row, and (3) column. However, for practical reasons, this data is often structured in a flat array, like ImageData.data or a two-dimensional array where each subarray holds all the values for a specific band in row-major order. This library was created for two main purposes: (1) to provide a unified interface for querying this data regardless of its practical structure and (2) converting this data between different array layouts.

install

  1. npm install xdim

xdim layout syntax

Most of the functions in this library require that you specify the layout of the data using “xdim layout syntax” or “xdim syntax” for short. The format is simple with just a few main pieces:
1) The straight brackets [ and ] indicates an actual array or subarrays.
2) The comma , appears between [ and ] and means dimensions are interleaved in left-to-right major order.
3) Dimension names can be made of any letter A to Z, lowercased or uppercased, can include underscores, and don’t include spaces.

xdim layout syntax examples

Here’s a couple examples of the “xdim layout syntax”:

example: cars

You have an array of information about car models where the information is stored in subarrays:

  1. [
  2. ["Fusion", "Ford", "United States", "2005", "2020"]
  3. ["Versa", "Nissan", "Japan", "2006", "2021"]
  4. ]

The layout could be described as "[model][brand,maker,county,start_year,end_year]"

example: pixels

You have ImageData.data:

  1. [31, 9, 71, 255, 126, 42, 53, 255, 71, 74, 71, 255, ...]

The layout could be described as "[row,column,band]".

usage

This library provides the following functions: select, prepareSelect, clip, iterClip, transform, prepareData, update, and prepareUpdate.

select

Select is used to get the value at a given multi-dimensional point. The point is an object where each key is the name of a dimension with an index number. Index numbers start at zero and increase until we reach the end of the length in the dimension.

  1. import { select } from 'xdim';
  2. // satellite imagery data broken down by band
  3. const data = [
  4. [0, 123, 123, 162, ...], // red band
  5. [213, 41, 62, 124, ...], // green band
  6. [84, 52, 124, 235, ...] // blue band
  7. ];
  8. const result = select({
  9. data,
  10. // each band is a separate array
  11. // the values in a band are in row-major order
  12. layout: "[band][row,column]",
  13. sizes: {
  14. band: 3, // image has 3 bands (red, green, and blue)
  15. column: 100 // image is 100 pixels wide
  16. },
  17. point: {
  18. band: 2, // 3rd band (blue), where band index starts at zero
  19. row: 74, // 75th row from the top
  20. column: 63 // 64th column from the left
  21. }
  22. });

result is an object

  1. {
  2. // the actual value found in the array
  3. value: 62,
  4. // the index in the array where the value is found
  5. index: 7463,
  6. // a reference to the same array in the provided data
  7. parent: [84, 52, 124, 235, ... 62, ...]
  8. }

prepareSelect

The prepareSelect function is use to create a supercharged select function for some data. There is some
fixed cost to creating the function, so only use it if you think you will run several to many selects.

:sparkles: So what magic makes the prepared select statements so fast? We pre-generate
select functions, so that JavaScript compilers
can optimize the logical steps needed to lookup data. We then just bind the dimension names, sizes, and data to these “pre-compiled” functions.

  1. import { prepareSelect } from 'xdim';
  2. // satellite imagery data broken down by band
  3. const data = [
  4. [0, 123, 123, 162, ...], // red band
  5. [213, 41, 62, 124, ...], // green band
  6. [84, 52, 124, 235, ...] // blue band
  7. ];
  8. const select = prepareSelect({
  9. data,
  10. // each band is a separate array
  11. // the values in a band are in row-major order
  12. layout: "[band][row,column]",
  13. sizes: {
  14. band: 3, // image has 3 bands (red, green, and blue)
  15. column: 100 // image is 100 pixels wide
  16. }
  17. });
  18. const result = select({
  19. point: {
  20. band: 2, // 3rd band (blue), where band index starts at zero
  21. row: 74, // 75th row from the top
  22. column: 63 // 64th column from the left
  23. }
  24. });

result is an object

  1. {
  2. // the actual value found in the array
  3. value: 62,
  4. // the index in the array where the value is found
  5. index: 7463,
  6. // a reference to the same array in the provided data
  7. parent: [84, 52, 124, 235, ... 62, ...]
  8. }

clip

The clip function is used to pull out a subsection of the data within a hyperrectangle (i.e. multi-dimensional rectangle), which we call “rect”. The “rect” is defined by an object with dimension name keys and a numerical range. The range is “inclusive”, including the first and last numbers provided.

  1. import { clip } from 'xdim';
  2. // satellite imagery data broken down by band
  3. const data = [
  4. [0, 123, 123, 162, ...], // red band
  5. [213, 41, 62, 124, ...], // green band
  6. [84, 52, 124, 235, ...] // blue band
  7. ];
  8. const result = clip({
  9. data,
  10. // if you don't care about the structure of the returned data
  11. // or want to receive your results more quickly,
  12. // you can set flat to true, and it will return a flat array
  13. flat: false,
  14. // each band is a separate array
  15. // the values in a band are in row-major order
  16. layout: "[band][row,column]",
  17. sizes: {
  18. band: 3, // image has 3 bands (red, green, and blue)
  19. column: 100 // image is 100 pixels wide
  20. },
  21. rect: {
  22. band: [2, 2], // 3rd band (blue), where band index starts at zero
  23. row: [55, 74], // from the 56th to the 75th row (counting from the top)
  24. column: [60, 62] // from the 61st to the 63rd column (counting from the left)
  25. }
  26. });

result is an object

  1. {
  2. data: [
  3. // only one band was selected, so we only have one sub-array
  4. // because the original data combined all the rows in the same array
  5. // the result has the same structure
  6. // all the values in band 2 that fall within row 55 to row 74 and column 60 to 62
  7. [64, 27, 19, 23, 45, 82 ... ]
  8. ]
  9. }

iterClip

Like clip, but returns a flat iterator of values. Useful if you want to minimize memory usage and avoid creating a new array.

  1. import { iterClip } from 'xdim';
  2. // satellite imagery data broken down by band
  3. const data = [
  4. [0, 123, 123, 162, ...], // red band
  5. [213, 41, 62, 124, ...], // green band
  6. [84, 52, 124, 235, ...] // blue band
  7. ];
  8. const result = iterClip({
  9. data,
  10. // each band is a separate array
  11. // the values in a band are in row-major order
  12. layout: "[band][row,column]",
  13. sizes: {
  14. band: 3, // image has 3 bands (red, green, and blue)
  15. column: 100 // image is 100 pixels wide
  16. },
  17. rect: {
  18. band: [2, 2], // 3rd band (blue), where band index starts at zero
  19. row: [55, 74], // from the 56th to the 75th row (counting from the top)
  20. column: [60, 62] // from the 61st to the 63rd column (counting from the left)
  21. },
  22. // optional
  23. // order to return point values
  24. // in left-to-right major order
  25. order: ["band", "row", "column"]
  26. });

result is an iterator object

  1. // call the first value
  2. result.next();
  3. // { done: false, next: 64}
  4. // you can also use for of syntax
  5. for (let n of result) {
  6. // n is a number 27, then 19, then 23
  7. }

transform

If your data is a one dimensional array, you can transform to another using the transform function.
In the example below we transform from a flat array of ImageData.data to a truly 3-dimensional array of arrays of arrays representing bands, then rows, then columns.

  1. import { transform } from 'multdimensional-functions';
  2. // an array of image data red, green, blue, alpha, red, green, blue, alpha,...
  3. const data = [0, 213, 84, 255, 123, 41, 52, 255, 123, 62, 124, 255, 162, 124, 235, 255, ...];
  4. const result = transform({
  5. data,
  6. from: "[row,column,band]", // starting layout where all in one row with row-major order and interleaved bands
  7. to: "[band][row][column]", // final layout where each dimension is represented by arrays or subarrays and there is no interleaving of numbers inside the arrays
  8. sizes: {
  9. band: 4, // red, green, blue and alpha
  10. row: 768,
  11. column: 1024
  12. }
  13. });

result is an object

  1. {
  2. data: [
  3. // red band
  4. [
  5. [0, 123, 123, 162, ...] // first row of the red band
  6. [212, 124, 127, 92, ... ] // second row of the red band
  7. ],
  8. // green band
  9. [
  10. [ ... ],
  11. [ ... ]
  12. ],
  13. // blue band
  14. [
  15. [ ... ],
  16. [ ... ]
  17. ],
  18. // alpha band
  19. [
  20. [ ... ],
  21. [ ... ]
  22. ]
  23. ]
  24. }

prepareData

If you just want to create the outline or skeleton of your structure without filling it in with data, you can call the prepareData function.

  1. import { prepareData } from 'xdim';
  2. const result = prepareData({
  3. // the default fill value is undefined, but you can set it to zero, null, -99, an object, or really anything you want
  4. // in this example, the default fill value is the number -99
  5. fill: -99,
  6. layout: "[band][row][column]",
  7. sizes: {
  8. band: 4,
  9. column: 1024,
  10. row: 768
  11. },
  12. arrayTypes: ["Array", "Array", "Int8Array"] // optional
  13. });

Result is an object with an empty data object and shape array. The data object holds the multi-dimensional array of arrays.
The shape array is an array that describes the actual length of the arrays used to hold the data. (It is the actual practical length and not the theoretical length of the dimensions).

  1. {
  2. shape: [4, 768, 1024], // describes the actual length of each array
  3. data: [
  4. // first band
  5. [
  6. Int8Array[-99, -99, ... ], // band's first row of columns with length being the number of columns
  7. Int8Array[-99, -99, ... ], // band's second row
  8. .
  9. .
  10. .
  11. ],
  12. // second band
  13. [
  14. Int8Array[-99, -99, ... ], // band's first row of columns with length being the number of columns
  15. Int8Array[-99, -99, ... ], // band's second row
  16. ]
  17. ]

update

If you have a multi-dimensional data structure and want to change a value, use update.

  1. import { update } from 'xdim';
  2. // an image in RGBA Image Data Format
  3. const data = [128, 31, 382, 255, 48, 38, 58, 255, ...];
  4. update({
  5. // the structure that we will be modifying with a new value
  6. data,
  7. // layout describing one array in major order from row to column to band
  8. layout: "[row,column,band]",
  9. // a point in multi-dimensional space
  10. point: {
  11. band: 2, // the 3rd band or blue
  12. row: 4, // the 5th row
  13. column: 8, // the 9th column
  14. },
  15. sizes: {
  16. band: 4, // the 4 bands: red, green, blue and alpha
  17. row: 768, // the number of rows or height of the image
  18. column: 1024, // the number of columns or width of the image
  19. },
  20. // the value to insert at the specified point
  21. // it doesn't have to be a number
  22. value: 128
  23. });

prepareUpdate

The function prepareUpdate is to update as prepareSelect is to select. It returns an optimized update function.

  1. import { prepareUpdate } from 'xdim';
  2. // an image in RGBA Image Data Format
  3. const data = [128, 31, 382, 255, 48, 38, 58, 255, ...];
  4. const update = prepareUpdate({
  5. // the structure that we will be modifying with update calls
  6. data,
  7. // layout describing one array in major order from row to column to band
  8. layout: "[row,column,band]",
  9. sizes: {
  10. band: 4, // the 4 bands: red, green, blue and alpha
  11. row: 768, // the number of rows or height of the image
  12. column: 1024, // the number of columns or width of the image
  13. }
  14. });
  15. update({
  16. // a point in multi-dimensional space
  17. point: {
  18. band: 2, // the 3rd band or blue
  19. row: 4, // the 5th row
  20. column: 8, // the 9th column
  21. },
  22. // the value to insert at the specified point
  23. // it doesn't have to be a number
  24. value: 128
  25. });

used by

support

Post an issue at https://github.com/DanielJDufour/xdim/issues.