@vitessce/spatial-utils 3.1.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{deflate-6fd0bca9.js → deflate-3240dc7e.js} +1 -1
- package/dist/{index-551fa504.js → index-ca4e31e3.js} +1007 -82
- package/dist/index.js +15 -2
- package/dist/{jpeg-d67d9d82.js → jpeg-76bff48f.js} +1 -1
- package/dist/{lerc-522345a3.js → lerc-1ad8fc7c.js} +1 -1
- package/dist/{lzw-c3783269.js → lzw-c5e8dff0.js} +1 -1
- package/dist/{packbits-209c03d7.js → packbits-ccf26149.js} +1 -1
- package/dist/{raw-8e22b1c4.js → raw-446b6beb.js} +1 -1
- package/dist/{webimage-a5d2769c.js → webimage-bf1c5425.js} +1 -1
- package/dist-tsc/constants.d.ts +2 -0
- package/dist-tsc/constants.d.ts.map +1 -0
- package/dist-tsc/constants.js +12 -0
- package/dist-tsc/index.d.ts +3 -1
- package/dist-tsc/index.js +3 -1
- package/dist-tsc/layer-controller.d.ts +10 -0
- package/dist-tsc/layer-controller.d.ts.map +1 -1
- package/dist-tsc/layer-controller.js +32 -5
- package/dist-tsc/layer-controller.test.d.ts +2 -0
- package/dist-tsc/layer-controller.test.d.ts.map +1 -0
- package/dist-tsc/layer-controller.test.js +22 -0
- package/dist-tsc/spatial.d.ts +25 -4
- package/dist-tsc/spatial.d.ts.map +1 -1
- package/dist-tsc/spatial.js +124 -3
- package/dist-tsc/spatial.test.js +156 -1
- package/package.json +3 -3
- package/src/constants.js +14 -0
- package/src/index.js +17 -0
- package/src/layer-controller.js +34 -5
- package/src/layer-controller.test.js +24 -0
- package/src/spatial.js +141 -3
- package/src/spatial.test.js +160 -1
package/src/index.js
CHANGED
|
@@ -15,4 +15,21 @@ export {
|
|
|
15
15
|
getNgffAxes,
|
|
16
16
|
getNgffAxesForTiff,
|
|
17
17
|
coordinateTransformationsToMatrix,
|
|
18
|
+
physicalSizeToMatrix,
|
|
19
|
+
hexToRgb,
|
|
20
|
+
getStatsForResolution,
|
|
21
|
+
canLoadResolution,
|
|
22
|
+
normalizeCoordinateTransformations,
|
|
18
23
|
} from './spatial.js';
|
|
24
|
+
export {
|
|
25
|
+
toRgbUIString,
|
|
26
|
+
getSingleSelectionStats,
|
|
27
|
+
getMultiSelectionStats,
|
|
28
|
+
getPhysicalSizeScalingMatrix,
|
|
29
|
+
getBoundingCube,
|
|
30
|
+
abbreviateNumber,
|
|
31
|
+
filterSelection,
|
|
32
|
+
} from './layer-controller.js';
|
|
33
|
+
export {
|
|
34
|
+
DOMAINS,
|
|
35
|
+
} from './constants.js';
|
package/src/layer-controller.js
CHANGED
|
@@ -1,9 +1,37 @@
|
|
|
1
|
-
import { Matrix4 } from 'math.gl';
|
|
2
1
|
import { viv } from '@vitessce/gl';
|
|
2
|
+
import { fromEntries } from '@vitessce/utils';
|
|
3
|
+
import { Matrix4 } from 'math.gl';
|
|
4
|
+
|
|
5
|
+
// Returns an rgb string for display, and changes the color (arr)
|
|
6
|
+
// to use a grey for light theme + white color or if the colormap is on.
|
|
7
|
+
export const toRgbUIString = (on, arr, theme) => {
|
|
8
|
+
const color = on || (theme === 'light' && arr?.every(i => i === 255))
|
|
9
|
+
? [220, 220, 220]
|
|
10
|
+
: arr;
|
|
11
|
+
return `rgb(${color})`;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Ensure that the channel selection object does not have
|
|
16
|
+
* extra dimension keys, as this will cause Viv functions
|
|
17
|
+
* to throw errors about not being able to access the data.
|
|
18
|
+
* @param {*} loader
|
|
19
|
+
* @param {object} selection Mapping from dimension label to slice index.
|
|
20
|
+
* @returns {object} The filtered selection.
|
|
21
|
+
*/
|
|
22
|
+
export function filterSelection(loader, selection) {
|
|
23
|
+
if (!selection) {
|
|
24
|
+
return selection;
|
|
25
|
+
}
|
|
26
|
+
const data = Array.isArray(loader) ? loader[loader.length - 1] : loader;
|
|
27
|
+
const { labels } = data;
|
|
28
|
+
return fromEntries(Object.entries(selection).filter(([key]) => labels.includes(key)));
|
|
29
|
+
}
|
|
3
30
|
|
|
4
31
|
async function getSingleSelectionStats2D({ loader, selection }) {
|
|
5
32
|
const data = Array.isArray(loader) ? loader[loader.length - 1] : loader;
|
|
6
|
-
const
|
|
33
|
+
const filteredSelection = filterSelection(loader, selection);
|
|
34
|
+
const raster = await data.getRaster({ selection: filteredSelection });
|
|
7
35
|
const selectionStats = viv.getChannelStats(raster.data);
|
|
8
36
|
const { domain, contrastLimits: slider } = selectionStats;
|
|
9
37
|
return { domain, slider };
|
|
@@ -11,17 +39,18 @@ async function getSingleSelectionStats2D({ loader, selection }) {
|
|
|
11
39
|
|
|
12
40
|
async function getSingleSelectionStats3D({ loader, selection }) {
|
|
13
41
|
const lowResSource = loader[loader.length - 1];
|
|
42
|
+
const filteredSelection = filterSelection(lowResSource, selection);
|
|
14
43
|
const { shape, labels } = lowResSource;
|
|
15
44
|
// eslint-disable-next-line no-bitwise
|
|
16
45
|
const sizeZ = shape[labels.indexOf('z')] >> (loader.length - 1);
|
|
17
46
|
const raster0 = await lowResSource.getRaster({
|
|
18
|
-
selection: { ...
|
|
47
|
+
selection: { ...filteredSelection, z: 0 },
|
|
19
48
|
});
|
|
20
49
|
const rasterMid = await lowResSource.getRaster({
|
|
21
|
-
selection: { ...
|
|
50
|
+
selection: { ...filteredSelection, z: Math.floor(sizeZ / 2) },
|
|
22
51
|
});
|
|
23
52
|
const rasterTop = await lowResSource.getRaster({
|
|
24
|
-
selection: { ...
|
|
53
|
+
selection: { ...filteredSelection, z: Math.max(0, sizeZ - 1) },
|
|
25
54
|
});
|
|
26
55
|
const stats0 = viv.getChannelStats(raster0.data);
|
|
27
56
|
const statsMid = viv.getChannelStats(rasterMid.data);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { toRgbUIString } from './layer-controller.js';
|
|
3
|
+
|
|
4
|
+
const GREY = [220, 220, 220];
|
|
5
|
+
|
|
6
|
+
describe('layer-controller/RasterChannelController.js', () => {
|
|
7
|
+
describe('toRgbUIString()', () => {
|
|
8
|
+
it('Maps color value to itself when colormap off and not white with dark theme', () => {
|
|
9
|
+
expect(toRgbUIString(false, [200, 200, 200], 'dark')).toEqual(`rgb(${[200, 200, 200]})`);
|
|
10
|
+
});
|
|
11
|
+
it('Maps color value to grey when colormap on with dark theme', () => {
|
|
12
|
+
expect(toRgbUIString(true, [200, 200, 200], 'dark')).toEqual(`rgb(${GREY})`);
|
|
13
|
+
});
|
|
14
|
+
it('Maps color value to grey when colormap on with light theme', () => {
|
|
15
|
+
expect(toRgbUIString(true, [200, 200, 200], 'light')).toEqual(`rgb(${GREY})`);
|
|
16
|
+
});
|
|
17
|
+
it('Maps color value to grey when colormap off and white with light theme', () => {
|
|
18
|
+
expect(toRgbUIString(false, [255, 255, 255], 'light')).toEqual(`rgb(${GREY})`);
|
|
19
|
+
});
|
|
20
|
+
it('Maps color value to grey when colormap off and white with light theme', () => {
|
|
21
|
+
expect(toRgbUIString(false, [200, 200, 200], 'light')).toEqual(`rgb(${[200, 200, 200]})`);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
package/src/spatial.js
CHANGED
|
@@ -53,6 +53,40 @@ export function getSourceFromLoader(loader, level) {
|
|
|
53
53
|
return source;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export const getStatsForResolution = (loader, resolution) => {
|
|
57
|
+
const { shape, labels } = loader[resolution];
|
|
58
|
+
const height = shape[labels.indexOf('y')];
|
|
59
|
+
const width = shape[labels.indexOf('x')];
|
|
60
|
+
const depth = shape[labels.indexOf('z')];
|
|
61
|
+
// eslint-disable-next-line no-bitwise
|
|
62
|
+
const depthDownsampled = Math.max(1, depth >> resolution);
|
|
63
|
+
// Check memory allocation limits for Float32Array (used in XR3DLayer for rendering)
|
|
64
|
+
const totalBytes = 4 * height * width * depthDownsampled;
|
|
65
|
+
return {
|
|
66
|
+
height, width, depthDownsampled, totalBytes,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const canLoadResolution = (loader, resolution) => {
|
|
71
|
+
const {
|
|
72
|
+
totalBytes, height, width, depthDownsampled,
|
|
73
|
+
} = getStatsForResolution(
|
|
74
|
+
loader,
|
|
75
|
+
resolution,
|
|
76
|
+
);
|
|
77
|
+
const maxHeapSize = window.performance?.memory
|
|
78
|
+
&& window.performance?.memory?.jsHeapSizeLimit / 2;
|
|
79
|
+
const maxSize = maxHeapSize || (2 ** 31) - 1;
|
|
80
|
+
// 2048 is a normal texture size limit although some browsers go larger.
|
|
81
|
+
return (
|
|
82
|
+
totalBytes < maxSize
|
|
83
|
+
&& height <= 2048
|
|
84
|
+
&& depthDownsampled <= 2048
|
|
85
|
+
&& width <= 2048
|
|
86
|
+
&& depthDownsampled > 1
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
56
90
|
/**
|
|
57
91
|
* Helper method to determine whether pixel data is interleaved and rgb or not.
|
|
58
92
|
* @param {object} loader
|
|
@@ -77,6 +111,42 @@ export function isRgb(loader, channels) {
|
|
|
77
111
|
|
|
78
112
|
// From spatial/utils.js
|
|
79
113
|
|
|
114
|
+
export function physicalSizeToMatrix(xSize, ySize, zSize, xUnit, yUnit, zUnit) {
|
|
115
|
+
const hasZPhyscialSize = Boolean(zSize) && Boolean(zUnit);
|
|
116
|
+
const hasYPhyscialSize = Boolean(ySize) && Boolean(yUnit);
|
|
117
|
+
const hasXPhyscialSize = Boolean(xSize) && Boolean(xUnit);
|
|
118
|
+
if (!hasXPhyscialSize || !hasYPhyscialSize) {
|
|
119
|
+
return (new Matrix4()).identity();
|
|
120
|
+
}
|
|
121
|
+
const sizes = [
|
|
122
|
+
unit(`${xSize} ${xUnit}`.replace('µ', 'u')),
|
|
123
|
+
unit(`${ySize} ${yUnit}`.replace('µ', 'u')),
|
|
124
|
+
];
|
|
125
|
+
if (hasZPhyscialSize) {
|
|
126
|
+
sizes.push(unit(`${zSize} ${zUnit}`.replace('µ', 'u')));
|
|
127
|
+
}
|
|
128
|
+
// Find the ratio of the sizes to get the scaling factor.
|
|
129
|
+
const scale = sizes.map(i => divide(i, unit('1 um')));
|
|
130
|
+
|
|
131
|
+
// TODO: is this still needed
|
|
132
|
+
// sizes are special objects with own equals method - see `unit` in declaration
|
|
133
|
+
if (!sizes[0].equals(sizes[1])) {
|
|
134
|
+
// Handle scaling in the Y direction for non-square pixels
|
|
135
|
+
scale[1] = divide(sizes[1], sizes[0]);
|
|
136
|
+
}
|
|
137
|
+
// END TODO: is this still needed
|
|
138
|
+
|
|
139
|
+
// Add in z dimension needed for Matrix4 scale API.
|
|
140
|
+
if (!scale[2]) {
|
|
141
|
+
scale[2] = 1;
|
|
142
|
+
}
|
|
143
|
+
// no need to store/use identity scaling
|
|
144
|
+
if (isEqual(scale, [1, 1, 1])) {
|
|
145
|
+
return (new Matrix4()).identity();
|
|
146
|
+
}
|
|
147
|
+
return new Matrix4().scale([...scale]);
|
|
148
|
+
}
|
|
149
|
+
|
|
80
150
|
function getMetaWithTransformMatrices(imageMeta, imageLoaders) {
|
|
81
151
|
// Do not fill in transformation matrices if any of the layers specify one.
|
|
82
152
|
const sources = imageLoaders.map(loader => getSourceFromLoader(loader));
|
|
@@ -367,11 +437,11 @@ export function getNgffAxesForTiff(dimOrder) {
|
|
|
367
437
|
/**
|
|
368
438
|
* Convert an array of coordinateTransformations objects to a 16-element
|
|
369
439
|
* plain JS array using Matrix4 linear algebra transformation functions.
|
|
370
|
-
* @param {object[]} coordinateTransformations List of objects matching the
|
|
440
|
+
* @param {object[]|undefined} coordinateTransformations List of objects matching the
|
|
371
441
|
* OME-NGFF v0.4 coordinateTransformations spec.
|
|
372
|
-
* @param {object[]} axes Axes in OME-NGFF v0.4 format, objects
|
|
442
|
+
* @param {object[]|undefined} axes Axes in OME-NGFF v0.4 format, objects
|
|
373
443
|
* with { type, name }.
|
|
374
|
-
* @returns {
|
|
444
|
+
* @returns {Matrix4} Array of 16 numbers representing the Matrix4.
|
|
375
445
|
*/
|
|
376
446
|
export function coordinateTransformationsToMatrix(coordinateTransformations, axes) {
|
|
377
447
|
let mat = (new Matrix4()).identity();
|
|
@@ -416,3 +486,71 @@ export function coordinateTransformationsToMatrix(coordinateTransformations, axe
|
|
|
416
486
|
}
|
|
417
487
|
return mat;
|
|
418
488
|
}
|
|
489
|
+
|
|
490
|
+
export function hexToRgb(hex) {
|
|
491
|
+
const result = /^#?([A-F\d]{2})([A-F\d]{2})([A-F\d]{2})$/i.exec(hex);
|
|
492
|
+
return [
|
|
493
|
+
parseInt(result[1].toLowerCase(), 16),
|
|
494
|
+
parseInt(result[2].toLowerCase(), 16),
|
|
495
|
+
parseInt(result[3].toLowerCase(), 16),
|
|
496
|
+
];
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Normalize coordinate transformations to the OME-NGFF v0.4 format,
|
|
501
|
+
* despite potentially being in the new format proposed in
|
|
502
|
+
* https://github.com/ome/ngff/pull/138 (As of 2023-09-02).
|
|
503
|
+
* @param {object[]|undefined} coordinateTransformations Value of
|
|
504
|
+
* multiscales[0].coordinateTransformations in either OME-NGFF v0.4 format
|
|
505
|
+
* or that proposed in https://github.com/ome/ngff/pull/138.
|
|
506
|
+
* @param {object[]} datasets Value of multiscales[0].datasets in OME-NGFF v0.4 format.
|
|
507
|
+
* @returns {object[]} Array of coordinateTransformations in OME-NGFF v0.4 format.
|
|
508
|
+
*/
|
|
509
|
+
export function normalizeCoordinateTransformations(coordinateTransformations, datasets) {
|
|
510
|
+
// "The transformations in the list are applied sequentially and in order."
|
|
511
|
+
// Reference: https://ngff.openmicroscopy.org/0.4/index.html#trafo-md
|
|
512
|
+
let result = [];
|
|
513
|
+
|
|
514
|
+
if (Array.isArray(coordinateTransformations)) {
|
|
515
|
+
result = coordinateTransformations.map((transform) => {
|
|
516
|
+
if (transform.input && transform.output) {
|
|
517
|
+
// This is a new-style coordinate transformation.
|
|
518
|
+
// (As proposed in https://github.com/ome/ngff/pull/138)
|
|
519
|
+
const { type } = transform;
|
|
520
|
+
if (type === 'translation') {
|
|
521
|
+
return {
|
|
522
|
+
type,
|
|
523
|
+
translation: transform.translation,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
if (type === 'scale') {
|
|
527
|
+
return {
|
|
528
|
+
type,
|
|
529
|
+
scale: transform.scale,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
if (type === 'identity') {
|
|
533
|
+
return { type };
|
|
534
|
+
}
|
|
535
|
+
console.warn(`Coordinate transformation type "${type}" is not supported.`);
|
|
536
|
+
}
|
|
537
|
+
// Assume it was already an old-style (NGFF v0.4) coordinate transformation.
|
|
538
|
+
return transform;
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (Array.isArray(datasets?.[0]?.coordinateTransformations)) {
|
|
543
|
+
// "Datasets SHOULD define a transformation from array space
|
|
544
|
+
// to their "native physical space."
|
|
545
|
+
// This transformation SHOULD describe physical pixel spacing
|
|
546
|
+
// and origin only, and therefore SHOULD consist of
|
|
547
|
+
// `scale` and/or `translation` types only.""
|
|
548
|
+
// Reference: https://github.com/ome/ngff/blob/b92f540dc95440f2d6b7012185b09c2b862aa744/latest/transform-details.bs#L99
|
|
549
|
+
|
|
550
|
+
result = [
|
|
551
|
+
...datasets[0].coordinateTransformations,
|
|
552
|
+
...result,
|
|
553
|
+
];
|
|
554
|
+
}
|
|
555
|
+
return result;
|
|
556
|
+
}
|
package/src/spatial.test.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
square,
|
|
4
|
+
coordinateTransformationsToMatrix,
|
|
5
|
+
normalizeCoordinateTransformations,
|
|
6
|
+
} from './spatial.js';
|
|
3
7
|
|
|
4
8
|
const defaultAxes = [
|
|
5
9
|
{ type: 'time', name: 't' },
|
|
@@ -43,4 +47,159 @@ describe('Spatial.js', () => {
|
|
|
43
47
|
]);
|
|
44
48
|
});
|
|
45
49
|
});
|
|
50
|
+
describe('normalizeCoordinateTransformations', () => {
|
|
51
|
+
it('does nothing for OME-NGFF v0.4 input', () => {
|
|
52
|
+
const datasets = [
|
|
53
|
+
{
|
|
54
|
+
coordinateTransformations: [
|
|
55
|
+
{
|
|
56
|
+
scale: [
|
|
57
|
+
1,
|
|
58
|
+
0.5002025531914894,
|
|
59
|
+
0.3603981534640209,
|
|
60
|
+
0.3603981534640209,
|
|
61
|
+
],
|
|
62
|
+
type: 'scale',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
path: '0',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
coordinateTransformations: [
|
|
69
|
+
{
|
|
70
|
+
scale: [
|
|
71
|
+
1,
|
|
72
|
+
0.5002025531914894,
|
|
73
|
+
0.7207963069280418,
|
|
74
|
+
0.7207963069280418,
|
|
75
|
+
],
|
|
76
|
+
type: 'scale',
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
path: '1',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
coordinateTransformations: [
|
|
83
|
+
{
|
|
84
|
+
scale: [
|
|
85
|
+
1,
|
|
86
|
+
0.5002025531914894,
|
|
87
|
+
1.4415926138560835,
|
|
88
|
+
1.4415926138560835,
|
|
89
|
+
],
|
|
90
|
+
type: 'scale',
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
path: '2',
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
expect(normalizeCoordinateTransformations(undefined, datasets)).toEqual([
|
|
97
|
+
// Here, we expect only the first dataset to be used.
|
|
98
|
+
// However, we should eventually support transforms
|
|
99
|
+
// specified for each dataset individually,
|
|
100
|
+
// since there could in theory be irregular ways of downsampling.
|
|
101
|
+
{
|
|
102
|
+
scale: [
|
|
103
|
+
1,
|
|
104
|
+
0.5002025531914894,
|
|
105
|
+
0.3603981534640209,
|
|
106
|
+
0.3603981534640209,
|
|
107
|
+
],
|
|
108
|
+
type: 'scale',
|
|
109
|
+
},
|
|
110
|
+
]);
|
|
111
|
+
});
|
|
112
|
+
it('transforms SpatialData input', () => {
|
|
113
|
+
const newCoordinateTransformations = [
|
|
114
|
+
{
|
|
115
|
+
input: {
|
|
116
|
+
axes: [
|
|
117
|
+
{
|
|
118
|
+
name: 'c',
|
|
119
|
+
type: 'channel',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'y',
|
|
123
|
+
type: 'space',
|
|
124
|
+
unit: 'unit',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'x',
|
|
128
|
+
type: 'space',
|
|
129
|
+
unit: 'unit',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
name: 'cyx',
|
|
133
|
+
},
|
|
134
|
+
output: {
|
|
135
|
+
axes: [
|
|
136
|
+
{
|
|
137
|
+
name: 'c',
|
|
138
|
+
type: 'channel',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'y',
|
|
142
|
+
type: 'space',
|
|
143
|
+
unit: 'unit',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'x',
|
|
147
|
+
type: 'space',
|
|
148
|
+
unit: 'unit',
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
name: 'ST8059048',
|
|
152
|
+
},
|
|
153
|
+
scale: [
|
|
154
|
+
1.0,
|
|
155
|
+
8.670500183814605,
|
|
156
|
+
8.670500183814605,
|
|
157
|
+
],
|
|
158
|
+
type: 'scale',
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
const datasets = [
|
|
162
|
+
{
|
|
163
|
+
coordinateTransformations: [
|
|
164
|
+
{
|
|
165
|
+
scale: [
|
|
166
|
+
1.0,
|
|
167
|
+
1.0,
|
|
168
|
+
1.0,
|
|
169
|
+
],
|
|
170
|
+
type: 'scale',
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
path: '0',
|
|
174
|
+
},
|
|
175
|
+
];
|
|
176
|
+
expect(normalizeCoordinateTransformations(newCoordinateTransformations, datasets)).toEqual([
|
|
177
|
+
// Here, we expect only the first dataset to be used.
|
|
178
|
+
// However, we should eventually support transforms
|
|
179
|
+
// specified for each dataset individually,
|
|
180
|
+
// since there could in theory be irregular ways of downsampling.
|
|
181
|
+
{
|
|
182
|
+
scale: [
|
|
183
|
+
1.0,
|
|
184
|
+
1.0,
|
|
185
|
+
1.0,
|
|
186
|
+
],
|
|
187
|
+
type: 'scale',
|
|
188
|
+
},
|
|
189
|
+
// We expect the dataset transform to be prepended
|
|
190
|
+
// to the newCoordinateTransformations items.
|
|
191
|
+
// We do not check the input/output coordinate systems, but we should eventually allow
|
|
192
|
+
// the user to specify this somehow and use that information to filter which transforms
|
|
193
|
+
// are included here.
|
|
194
|
+
{
|
|
195
|
+
scale: [
|
|
196
|
+
1.0,
|
|
197
|
+
8.670500183814605,
|
|
198
|
+
8.670500183814605,
|
|
199
|
+
],
|
|
200
|
+
type: 'scale',
|
|
201
|
+
},
|
|
202
|
+
]);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
46
205
|
});
|