Skip to content

Commit a050645

Browse files
hackermdhackermd
authored andcommitted
Fix spatial coordinates
* replace Circle with Ellipsoid (CIRCLE graphic type does not exist for SCOORD3D) * add fiducialUID property to Scoord3D (Fiducial UID is a type 3 attribute but may come handy for tracking SCOORD3D items) * replace UUID with DICOM UUID derived UID (WARNING: in current implementation the UID is not derived from a real) * ensure spatial coordinates are positive numbers
1 parent 176a034 commit a050645

File tree

6 files changed

+279
-199
lines changed

6 files changed

+279
-199
lines changed

src/api.js

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,28 @@ import { toStringXY } from 'ol/coordinate';
2929

3030
import { formatImageMetadata } from './metadata.js';
3131
import { ROI } from './roi.js';
32-
import { generateUuid } from './utils.js';
32+
import { generateUID } from './utils.js';
3333
import {
3434
Point,
3535
Multipoint,
3636
Polyline,
3737
Polygon,
38-
Circle,
38+
Ellipsoid,
3939
Ellipse
4040
} from './scoord3d.js';
4141

4242
import DICOMwebClient from 'dicomweb-client/build/dicomweb-client.js'
4343

4444

4545
function _geometry2Scoord3d(geometry, pyramid) {
46-
const referencedFrameOfReferenceUID = pyramid[pyramid.length-1].frameOfReferenceUID;
46+
const frameOfReferenceUID = pyramid[pyramid.length-1].frameOfReferenceUID;
4747
const type = geometry.getType();
4848
if (type === 'Point') {
4949
let coordinates = geometry.getCoordinates();
5050
coordinates = _geometryCoordinates2scoord3dCoordinates(coordinates, pyramid);
5151
return new Point({
5252
coordinates,
53-
referencedFrameOfReferenceUID: referencedFrameOfReferenceUID
53+
frameOfReferenceUID: frameOfReferenceUID
5454
});
5555
} else if (type === 'Polygon') {
5656
/*
@@ -62,30 +62,34 @@ function _geometry2Scoord3d(geometry, pyramid) {
6262
});
6363
return new Polygon({
6464
coordinates,
65-
referencedFrameOfReferenceUID: referencedFrameOfReferenceUID
65+
frameOfReferenceUID: frameOfReferenceUID
6666
});
6767
} else if (type === 'LineString') {
6868
let coordinates = geometry.getCoordinates().map(c => {
6969
return _geometryCoordinates2scoord3dCoordinates(c, pyramid);
7070
});
7171
return new Polyline({
7272
coordinates,
73-
referencedFrameOfReferenceUID: referencedFrameOfReferenceUID
73+
frameOfReferenceUID: frameOfReferenceUID
7474
});
7575
} else if (type === 'Circle') {
76-
// chunking the Flat Coordinates into two arrays within 3 elements each
77-
let coordinates = geometry.getFlatCoordinates().reduce((all,one,i) => {
78-
const ch = Math.floor(i/2)
79-
all[ch] = [].concat((all[ch]||[]),one)
80-
return all
81-
}, [])
76+
let centerCoordinate = geometry.getCenter();
77+
let radius = geometry.getRadius();
78+
// Endpoints of major and minor axis of the ellipse.
79+
// In case of a circle they both have the same length.
80+
let coordinates = [
81+
[centerCoordinate[0] - radius, centerCoordinate[1]],
82+
[centerCoordinate[0] + radius, centerCoordinate[1]],
83+
[centerCoordinate[0], centerCoordinate[1] - radius],
84+
[centerCoordinate[0], centerCoordinate[1] + radius],
85+
];
8286
coordinates = coordinates.map(c => {
8387
c.push(0)
8488
return _geometryCoordinates2scoord3dCoordinates(c, pyramid)
8589
})
86-
return new Circle({
90+
return new Ellipse({
8791
coordinates,
88-
referencedFrameOfReferenceUID: referencedFrameOfReferenceUID
92+
frameOfReferenceUID: frameOfReferenceUID
8993
});
9094
} else {
9195
// TODO: Combine multiple points into MULTIPOINT.
@@ -109,12 +113,28 @@ function _scoord3d2Geometry(scoord3d, pyramid) {
109113
return _scoord3dCoordinates2geometryCoordinates(d, pyramid);
110114
});
111115
return new PolygonGeometry([coordinates]);
112-
} else if (type === 'CIRCLE') {
113-
let coordinates = data.map(d => {
116+
} else if (type === 'ELLIPSE') {
117+
// TODO: ensure that the ellipse represents a circle, i.e. that
118+
// major and minor axis form a right angle and have the same length
119+
let majorAxisCoordinates = data.slice(0, 2);
120+
let minorAxisCoordinates = data.slice(2, 4);
121+
// Circle is defined by two points: the center point and a point on the
122+
// circumference.
123+
let point1 = majorAxisCoordinates[0];
124+
let point2 = majorAxisCoordinates[1];
125+
let coordinates = [
126+
[
127+
(point1[0] + point2[0]) / parseFloat(2),
128+
(point1[1] + point2[1]) / parseFloat(2),
129+
1
130+
],
131+
point2
132+
];
133+
coordinates = coordinates.map(d => {
114134
return _scoord3dCoordinates2geometryCoordinates(d, pyramid);
115-
})
135+
});
116136
// to flat coordinates
117-
coordinates = [...coordinates[0].slice(0,2), ...coordinates[1].slice(0,2)]
137+
coordinates = [...coordinates[0].slice(0,2), ...coordinates[1].slice(0,2)];
118138

119139
// flat coordinates in combination with opt_layout and no opt_radius are also accepted
120140
// and internaly it calculates the Radius
@@ -125,7 +145,7 @@ function _scoord3d2Geometry(scoord3d, pyramid) {
125145
}
126146

127147
function _geometryCoordinates2scoord3dCoordinates(coordinates, pyramid) {
128-
return _coordinateFormatGeometry2Scoord3d([coordinates[0] + 1, -coordinates[1], coordinates[2]], pyramid);
148+
return _coordinateFormatGeometry2Scoord3d([coordinates[0] + 1, coordinates[1], coordinates[2]], pyramid);
129149
}
130150

131151
function _scoord3dCoordinates2geometryCoordinates(coordinates, pyramid) {
@@ -159,7 +179,7 @@ function _coordinateFormatScoord3d2Geometry(coordinates, pyramid) {
159179
}
160180
coordinates.map(coord =>{
161181
let x = (coord[0] / pyramid[pyramid.length-1].pixelSpacing[0] - 1);
162-
let y = (coord[1] / pyramid[pyramid.length-1].pixelSpacing[1] - 1);
182+
let y = -(coord[1] / pyramid[pyramid.length-1].pixelSpacing[1] - 1);
163183
let z = coord[2];
164184
coordinates = [x, y, z];
165185
});
@@ -232,7 +252,7 @@ class VLWholeSlideMicroscopyImageViewer {
232252
// The ID may have already been set when drawn. However, features could
233253
// have also been added without a draw event.
234254
if (e.element.getId() === undefined) {
235-
e.element.setId(generateUuid());
255+
e.element.setId(generateUID());
236256
}
237257
});
238258

@@ -737,7 +757,7 @@ class VLWholeSlideMicroscopyImageViewer {
737757

738758
//attaching openlayers events handling
739759
this[_interactions].draw.on('drawend', (e) => {
740-
e.feature.setId(generateUuid());
760+
e.feature.setId(generateUID());
741761
publish(container, EVENT.ROI_DRAWN, _getROIFromFeature(e.feature, this._pyramid));
742762
});
743763

src/dicom-microscopy-viewer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
Multipoint,
66
Polyline,
77
Polygon,
8-
Circle,
8+
Ellipsoid,
99
Ellipse,
1010
} from './scoord3d.js';
1111

@@ -17,7 +17,7 @@ let scoord3d = {
1717
Multipoint,
1818
Polyline,
1919
Polygon,
20-
Circle,
20+
Ellipsoid,
2121
Ellipse
2222
};
2323
let roi = {

src/roi.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { generateUuid } from './utils.js';
1+
import { generateUID } from './utils.js';
22

33

44
/* Region of interest.
@@ -14,7 +14,7 @@ class ROI {
1414
console.error('spatial coordinates are required for ROI')
1515
}
1616
if (!('uid' in options)) {
17-
this.uid = generateUuid();
17+
this.uid = generateUID();
1818
} else {
1919
if (!(typeof options.uid === 'string' || options.uid instanceof String)) {
2020
throw new Error('uid of ROI must be a string')

0 commit comments

Comments
 (0)