{"id":18417748,"url":"https://github.com/danieljdufour/xdim","last_synced_at":"2025-06-13T16:36:34.660Z","repository":{"id":44357497,"uuid":"390475974","full_name":"DanielJDufour/xdim","owner":"DanielJDufour","description":"Multi-Dimensional Functions.  Create, Query, and Transform Multi-Dimensional Data.","archived":false,"fork":false,"pushed_at":"2023-01-16T02:51:42.000Z","size":111,"stargazers_count":9,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-22T18:44:09.120Z","etag":null,"topics":["array","binary","data","dimensions","format","formatter","functions","image","javascript","js","layout","math","multidimensional","ndarray","rearrange","reorganize","reshape","shape","theory"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DanielJDufour.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-07-28T19:18:32.000Z","updated_at":"2023-12-02T04:10:31.000Z","dependencies_parsed_at":"2023-02-10T00:45:49.650Z","dependency_job_id":null,"html_url":"https://github.com/DanielJDufour/xdim","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielJDufour%2Fxdim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielJDufour%2Fxdim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielJDufour%2Fxdim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielJDufour%2Fxdim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DanielJDufour","download_url":"https://codeload.github.com/DanielJDufour/xdim/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247653381,"owners_count":20973819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["array","binary","data","dimensions","format","formatter","functions","image","javascript","js","layout","math","multidimensional","ndarray","rearrange","reorganize","reshape","shape","theory"],"created_at":"2024-11-06T04:11:04.084Z","updated_at":"2025-04-07T12:32:49.728Z","avatar_url":"https://github.com/DanielJDufour.png","language":"JavaScript","readme":"# xdim\n\u003e Multi-Dimensional Functions\n\n# motivation\nI 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](https://developer.mozilla.org/en-US/docs/Web/API/ImageData/data) or a two-dimensional array where each subarray holds all the values for a specific band in [row-major order](https://en.wikipedia.org/wiki/Row-_and_column-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.\n\n# install\n```bash\nnpm install xdim\n```\n\n# xdim layout syntax\nMost of the functions in this library require that you specify the layout of the data using \u003cb\u003e\"xdim layout syntax\"\u003c/b\u003e or \u003cb\u003e\"xdim syntax\"\u003c/b\u003e for short.  The format is simple with just a few main pieces:\n1) The straight brackets `[` and `]` indicates an actual array or subarrays.\n2) The comma `,` appears between `[` and `]` and means dimensions are interleaved in left-to-right major order.\n3) Dimension names can be made of any letter A to Z, lowercased or uppercased, can include underscores, and don't include spaces.\n\n### xdim layout syntax examples\nHere's a couple examples of the \u003cb\u003e\"xdim layout syntax\"\u003c/b\u003e:\n\n#### example: cars\nYou have an array of information about car models where the information is stored in subarrays:\n```js\n[\n  [\"Fusion\", \"Ford\", \"United States\", \"2005\", \"2020\"]\n  [\"Versa\", \"Nissan\", \"Japan\", \"2006\", \"2021\"]\n]\n```\nThe layout could be described as `\"[model][brand,maker,county,start_year,end_year]\"`\n  \n#### example: pixels\nYou have [ImageData.data](https://developer.mozilla.org/en-US/docs/Web/API/ImageData/data):\n```js\n[31, 9, 71, 255, 126, 42, 53, 255, 71, 74, 71, 255, ...]\n```\nThe layout could be described as `\"[row,column,band]\"`.\n\n\n# usage\nThis library provides the following functions: [select](#select), [prepareSelect](#prepareSelect), [clip](#clip), [iterClip](#iterClip), [transform](#transform), [prepareData](#prepareData), [update](#update), and [prepareUpdate](#prepareUpdate).\n\n## select\nSelect 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.\n\n```javascript\nimport { select } from 'xdim';\n\n// satellite imagery data broken down by band\nconst data = [\n  [0, 123, 123, 162, ...], // red band\n  [213, 41, 62, 124, ...], // green band\n  [84, 52, 124, 235, ...] // blue band\n];\n\nconst result = select({\n  data,\n\n  // each band is a separate array\n  // the values in a band are in row-major order\n  layout: \"[band][row,column]\",\n  \n  sizes: {\n    band: 3, // image has 3 bands (red, green, and blue)\n    column: 100 // image is 100 pixels wide\n  },\n \n  point: {\n    band: 2, // 3rd band (blue), where band index starts at zero\n    row: 74, // 75th row from the top\n    column: 63 // 64th column from the left\n  }\n});\n```\nresult is an object\n```js\n{\n  // the actual value found in the array\n  value: 62,\n\n  // the index in the array where the value is found\n  index: 7463,\n  \n  // a reference to the same array in the provided data\n  parent: [84, 52, 124, 235, ... 62, ...]\n}\n```\n\n## prepareSelect\nThe `prepareSelect` function is use to create a supercharged select function for some data.  There is some\nfixed cost to creating the function, so only use it if you think you will run several to many selects.\n\n\n:sparkles: So what magic makes the prepared select statements so fast?  We pre-generate\n[select functions](https://github.com/DanielJDufour/xdim/blob/main/src/prepared-select-funcs.js), so that JavaScript compilers\ncan optimize the logical steps needed to lookup data.  We then just [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind) the dimension names, sizes, and data to these \"pre-compiled\" functions.\n\n```javascript\nimport { prepareSelect } from 'xdim';\n\n// satellite imagery data broken down by band\nconst data = [\n  [0, 123, 123, 162, ...], // red band\n  [213, 41, 62, 124, ...], // green band\n  [84, 52, 124, 235, ...] // blue band\n];\n\nconst select = prepareSelect({\n  data,\n\n  // each band is a separate array\n  // the values in a band are in row-major order\n  layout: \"[band][row,column]\",\n  \n  sizes: {\n    band: 3, // image has 3 bands (red, green, and blue)\n    column: 100 // image is 100 pixels wide\n  }\n});\n\nconst result = select({\n  point: {\n    band: 2, // 3rd band (blue), where band index starts at zero\n    row: 74, // 75th row from the top\n    column: 63 // 64th column from the left\n  }\n});\n```\nresult is an object\n```js\n{\n  // the actual value found in the array\n  value: 62,\n\n  // the index in the array where the value is found\n  index: 7463,\n  \n  // a reference to the same array in the provided data\n  parent: [84, 52, 124, 235, ... 62, ...]\n}\n```\n\n## clip\nThe `clip` function is used to pull out a subsection of the data within a [hyperrectangle](https://en.wikipedia.org/wiki/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.\n```javascript\nimport { clip } from 'xdim';\n\n// satellite imagery data broken down by band\nconst data = [\n  [0, 123, 123, 162, ...], // red band\n  [213, 41, 62, 124, ...], // green band\n  [84, 52, 124, 235, ...] // blue band\n];\n\nconst result = clip({\n  data,\n\n  // if you don't care about the structure of the returned data\n  // or want to receive your results more quickly,\n  // you can set flat to true, and it will return a flat array\n  flat: false,\n\n  // each band is a separate array\n  // the values in a band are in row-major order\n  layout: \"[band][row,column]\",\n  \n  sizes: {\n    band: 3, // image has 3 bands (red, green, and blue)\n    column: 100 // image is 100 pixels wide\n  },\n \n  rect: {\n    band: [2, 2], // 3rd band (blue), where band index starts at zero\n    row: [55, 74], // from the 56th to the 75th row (counting from the top)\n    column: [60, 62] // from the 61st to the 63rd column (counting from the left)\n  }\n});\n```\nresult is an object\n```js\n{\n  data: [\n    // only one band was selected, so we only have one sub-array\n    // because the original data combined all the rows in the same array\n    // the result has the same structure\n\n    // all the values in band 2 that fall within row 55 to row 74 and column 60 to 62\n    [64, 27, 19, 23, 45, 82 ... ]\n  ]\n}\n```\n\n## iterClip\nLike [clip](#clip), but returns a flat iterator of values.  Useful if you want to minimize memory usage and avoid creating a new array.\n```javascript\nimport { iterClip } from 'xdim';\n\n// satellite imagery data broken down by band\nconst data = [\n  [0, 123, 123, 162, ...], // red band\n  [213, 41, 62, 124, ...], // green band\n  [84, 52, 124, 235, ...] // blue band\n];\n\nconst result = iterClip({\n  data,\n\n  // each band is a separate array\n  // the values in a band are in row-major order\n  layout: \"[band][row,column]\",\n  \n  sizes: {\n    band: 3, // image has 3 bands (red, green, and blue)\n    column: 100 // image is 100 pixels wide\n  },\n \n  rect: {\n    band: [2, 2], // 3rd band (blue), where band index starts at zero\n    row: [55, 74], // from the 56th to the 75th row (counting from the top)\n    column: [60, 62] // from the 61st to the 63rd column (counting from the left)\n  },\n\n  // optional\n  // order to return point values\n  // in left-to-right major order\n  order: [\"band\", \"row\", \"column\"]\n});\n```\nresult is an iterator object\n```js\n\n// call the first value\nresult.next();\n// { done: false, next: 64}\n\n// you can also use for of syntax\nfor (let n of result) {\n  // n is a number 27, then 19, then 23\n}\n```\n\n## transform\nIf your data is a one dimensional array, you can transform to another using the transform function.\nIn the example below we transform from a flat array of [ImageData.data](https://developer.mozilla.org/en-US/docs/Web/API/ImageData/data) to a truly 3-dimensional array of arrays of arrays representing bands, then rows, then columns.\n\n```javascript\nimport { transform } from 'multdimensional-functions';\n\n// an array of image data red, green, blue, alpha, red, green, blue, alpha,...\nconst data = [0, 213, 84, 255, 123, 41, 52, 255, 123, 62, 124, 255, 162, 124, 235, 255, ...];\n\nconst result = transform({\n  data,\n  from: \"[row,column,band]\", // starting layout where all in one row with row-major order and interleaved bands\n  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\n  sizes: {\n    band: 4, // red, green, blue and alpha\n    row: 768,\n    column: 1024\n  }\n});\n```\nresult is an object\n```js\n{\n  data: [\n    // red band\n    [\n      [0, 123, 123, 162, ...] // first row of the red band\n      [212, 124, 127, 92, ... ] // second row of the red band\n    ],\n\n    // green band\n    [\n      [ ... ],\n      [ ... ]\n    ],\n\n    // blue band\n    [\n      [ ... ],\n      [ ... ]\n    ],\n\n    // alpha band\n    [\n      [ ... ],\n      [ ... ]\n    ]\n  ]\n}\n```\n\n## prepareData\nIf you just want to create the outline or skeleton of your structure without filling it in with data, you can call the prepareData function.\n```js\nimport { prepareData } from 'xdim';\n\nconst result = prepareData({\n  // the default fill value is undefined, but you can set it to zero, null, -99, an object, or really anything you want\n  // in this example, the default fill value is the number -99\n  fill: -99, \n  layout: \"[band][row][column]\",\n  sizes: {\n    band: 4,\n    column: 1024,\n    row: 768\n  },\n  arrayTypes: [\"Array\", \"Array\", \"Int8Array\"] // optional\n});\n```\nResult is an object with an empty data object and shape array.  The data object holds the multi-dimensional array of arrays.\nThe 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).\n```\n{\n  shape: [4, 768, 1024], // describes the actual length of each array\n  data: [\n    // first band\n    [\n      Int8Array[-99, -99, ... ], // band's first row of columns with length being the number of columns\n      Int8Array[-99, -99, ... ], // band's second row\n      .\n      .\n      .\n    ],\n    \n    // second band\n    [\n      Int8Array[-99, -99, ... ], // band's first row of columns with length being the number of columns\n      Int8Array[-99, -99, ... ], // band's second row\n    ]\n  ]\n```\n\n## update\nIf you have a multi-dimensional data structure and want to change a value, use `update`.\n```js\nimport { update } from 'xdim';\n\n// an image in RGBA Image Data Format\nconst data = [128, 31, 382, 255, 48, 38, 58, 255, ...];\n\nupdate({\n  // the structure that we will be modifying with a new value\n  data,\n \n  // layout describing one array in major order from row to column to band\n  layout: \"[row,column,band]\",\n\n  // a point in multi-dimensional space\n  point: {\n    band: 2, // the 3rd band or blue\n    row: 4, // the 5th row\n    column: 8, // the 9th column\n  },\n \n  sizes: {\n    band: 4, // the 4 bands: red, green, blue and alpha\n    row: 768, // the number of rows or height of the image\n    column: 1024, // the number of columns or width of the image\n  },\n \n  // the value to insert at the specified point\n  // it doesn't have to be a number\n  value: 128 \n});\n```\n\n## prepareUpdate\nThe function `prepareUpdate` is to [update](#update) as [prepareSelect](#prepareSelect) is to [select](#select).  It returns an optimized update function.\n```js\nimport { prepareUpdate } from 'xdim';\n\n// an image in RGBA Image Data Format\nconst data = [128, 31, 382, 255, 48, 38, 58, 255, ...];\n\nconst update = prepareUpdate({\n  // the structure that we will be modifying with update calls\n  data,\n \n  // layout describing one array in major order from row to column to band\n  layout: \"[row,column,band]\",\n \n  sizes: {\n    band: 4, // the 4 bands: red, green, blue and alpha\n    row: 768, // the number of rows or height of the image\n    column: 1024, // the number of columns or width of the image\n  }\n});\n\nupdate({\n  // a point in multi-dimensional space\n  point: {\n    band: 2, // the 3rd band or blue\n    row: 4, // the 5th row\n    column: 8, // the 9th column\n  },\n  \n  // the value to insert at the specified point\n  // it doesn't have to be a number\n  value: 128 \n});\n```\n\n# used by\n- [geowarp](https://github.com/danieljdufour/geowarp)\n- [georaster](https://github.com/geotiff/georaster)\n\n# support\nPost an issue at https://github.com/DanielJDufour/xdim/issues.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanieljdufour%2Fxdim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanieljdufour%2Fxdim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanieljdufour%2Fxdim/lists"}