Skip to content

Commit ec99a74

Browse files
committed
Improve performance of coordinate translation
1 parent e2f514f commit ec99a74

File tree

6 files changed

+167
-91
lines changed

6 files changed

+167
-91
lines changed

src/annotations/_AnnotationManager.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ import { areCodedConceptsEqual, getContentItemNameCodedConcept } from '../utils'
2222
const { Marker, Markup } = Enums
2323

2424
class _AnnotationManager {
25-
constructor ({ map, pyramid, drawingSource } = {}) {
25+
constructor ({ map, pyramid, affine, drawingSource } = {}) {
2626
const markupManager = new _MarkupManager({
2727
map,
2828
pyramid,
29+
affine,
2930
drawingSource,
3031
formatters: {
3132
[Marker.Arrow]: arrowFormat,
@@ -37,6 +38,7 @@ class _AnnotationManager {
3738
this.props = {
3839
map,
3940
pyramid,
41+
affine,
4042
markupManager
4143
}
4244

src/annotations/_AnnotationManager.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ describe('_AnnotationManager', () => {
7979
set: jest.fn()
8080
}
8181
map = { addOverlay: jest.fn(), addLayer: jest.fn() }
82+
// TODO
8283
_markupManager = new _MarkupManager()
8384
_annotationManager = new _AnnotationManager({
8485
map: {}

src/annotations/markups/_MarkupManager.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,18 @@ const _getShortestLineBetweenOverlayAndFeature = (feature, overlay) => {
6060
}
6161

6262
class _MarkupManager {
63-
constructor ({ map, pyramid, drawingSource, formatters, onClick, onStyle } = {}) {
63+
constructor ({
64+
map,
65+
pyramid,
66+
affine,
67+
drawingSource,
68+
formatters,
69+
onClick,
70+
onStyle
71+
} = {}) {
6472
this._map = map
6573
this._pyramid = pyramid
74+
this._affine = affine
6675
this._formatters = formatters
6776
this._drawingSource = drawingSource
6877

@@ -251,7 +260,12 @@ class _MarkupManager {
251260
const view = this._map.getView()
252261
const unitSuffix = _getUnitSuffix(view)
253262
const format = this._getFormatter(event.target)
254-
const output = format(event.target, unitSuffix, this._pyramid)
263+
const output = format(
264+
event.target,
265+
unitSuffix,
266+
this._pyramid,
267+
this._affine
268+
)
255269
this.update({
256270
feature,
257271
value: output,

src/annotations/markups/measurement.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import {
1515
*
1616
* @private
1717
*/
18-
export const _format = (feature, units, pyramid) => {
19-
const area = _getFeatureArea(feature, pyramid)
20-
const length = _getFeatureLength(feature, pyramid)
18+
export const _format = (feature, units, pyramid, affine) => {
19+
const area = _getFeatureArea(feature, pyramid, affine)
20+
const length = _getFeatureLength(feature, pyramid, affine)
2121
const value = length || area || 0
2222
return length
2323
? `${value.toFixed(2)} ${units}`
@@ -39,22 +39,23 @@ const _isMeasurement = (feature) =>
3939
/**
4040
* Measurement markup definition.
4141
*
42-
* @param {object} dependencies Shared dependencies
43-
* @param {object} dependencies.map Viewer's map instance
44-
* @param {object} dependencies.pyramid Pyramid metadata
45-
* @param {object} dependencies.markupManager MarkupManager shared instance
42+
* @param {object} dependencies - Shared dependencies
43+
* @param {object} dependencies.map - Viewer's map instance
44+
* @param {object} dependencies.pyramid - Pyramid metadata
45+
* @param {number[][]} dependencies.affine - 3x3 affine transformation matrix
46+
* @param {object} dependencies.markupManager - MarkupManager shared instance
4647
*
4748
* @private
4849
*/
49-
const MeasurementMarkup = ({ map, pyramid, markupManager }) => {
50+
const MeasurementMarkup = ({ map, pyramid, affine, markupManager }) => {
5051
return {
5152
onAdd: (feature) => {
5253
if (_isMeasurement(feature)) {
5354
const view = map.getView()
5455
const unitSuffix = _getUnitSuffix(view)
5556
markupManager.create({
5657
feature,
57-
value: _format(feature, unitSuffix, pyramid)
58+
value: _format(feature, unitSuffix, pyramid, affine)
5859
})
5960
}
6061
},
@@ -81,7 +82,7 @@ const MeasurementMarkup = ({ map, pyramid, markupManager }) => {
8182
const markup = markupManager.get(id)
8283
markupManager.update({
8384
feature,
84-
value: _format(feature, unitSuffix, pyramid),
85+
value: _format(feature, unitSuffix, pyramid, affine),
8586
coordinate: markup.overlay.getPosition()
8687
})
8788
},

src/scoord3dUtils.js

Lines changed: 41 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import LineStringGeometry from 'ol/geom/LineString'
77

88
import {
99
applyInverseTransform,
10-
applyTransform,
11-
buildInverseTransform,
12-
buildTransform
10+
applyTransform
1311
} from './utils.js'
1412

1513
/**
@@ -18,17 +16,22 @@ import {
1816
*
1917
* @param {object} feature - OpenLayers Feature
2018
* @param {Object[]} pyramid - Metadata for resolution levels of image pyramid
19+
* @param {number[][]} affine - 3x3 affine transformation matrix
2120
* @returns {Scoord3D} DICOM Microscopy Viewer Scoord3D
2221
* @private
2322
*/
24-
function _geometry2Scoord3d (feature, pyramid) {
23+
function _geometry2Scoord3d (feature, pyramid, affine) {
2524
const geometry = feature.getGeometry()
2625
console.info('map coordinates from pixel matrix to slide coordinate system')
2726
const frameOfReferenceUID = pyramid[pyramid.length - 1].FrameOfReferenceUID
2827
const type = geometry.getType()
2928
if (type === 'Point') {
3029
let coordinates = geometry.getCoordinates()
31-
coordinates = _geometryCoordinates2scoord3dCoordinates(coordinates, pyramid)
30+
coordinates = _geometryCoordinates2scoord3dCoordinates(
31+
coordinates,
32+
pyramid,
33+
affine
34+
)
3235
return new Point({
3336
coordinates,
3437
frameOfReferenceUID: frameOfReferenceUID
@@ -39,16 +42,15 @@ function _geometry2Scoord3d (feature, pyramid) {
3942
* Each subsequent linear ring defines a hole in the surface.
4043
*/
4144
const coordinates = geometry.getCoordinates()[0].map((c) => {
42-
return _geometryCoordinates2scoord3dCoordinates(c, pyramid)
45+
return _geometryCoordinates2scoord3dCoordinates(c, pyramid, affine)
4346
})
4447
return new Polygon({
4548
coordinates,
4649
frameOfReferenceUID: frameOfReferenceUID
4750
})
4851
} else if (type === 'LineString') {
4952
const coordinates = geometry.getCoordinates().map((c) => {
50-
const result = _geometryCoordinates2scoord3dCoordinates(c, pyramid)
51-
return result
53+
return _geometryCoordinates2scoord3dCoordinates(c, pyramid, affine)
5254
})
5355
return new Polyline({
5456
coordinates,
@@ -65,7 +67,7 @@ function _geometry2Scoord3d (feature, pyramid) {
6567
[center[0], center[1] + radius, 0]
6668
]
6769
coordinates = coordinates.map((c) => {
68-
return _geometryCoordinates2scoord3dCoordinates(c, pyramid)
70+
return _geometryCoordinates2scoord3dCoordinates(c, pyramid, affine)
6971
})
7072
return new Ellipse({
7173
coordinates,
@@ -83,25 +85,30 @@ function _geometry2Scoord3d (feature, pyramid) {
8385
*
8486
* @param {Scoord3D} scoord3d - DICOM Microscopy Viewer Scoord3D
8587
* @param {Object[]} pyramid - Metadata for resolution levels of image pyramid
88+
* @param {number[][]} affine - 3x3 affine transformation matrix
8689
* @returns {object} Openlayers Geometry
8790
* @private
8891
*/
89-
function _scoord3d2Geometry (scoord3d, pyramid) {
92+
function _scoord3d2Geometry (scoord3d, pyramid, affine) {
9093
console.info('map coordinates from slide coordinate system to pixel matrix')
9194
const type = scoord3d.graphicType
9295
const data = scoord3d.graphicData
9396

9497
if (type === 'POINT') {
95-
const coordinates = _scoord3dCoordinates2geometryCoordinates(data, pyramid)
98+
const coordinates = _scoord3dCoordinates2geometryCoordinates(
99+
data,
100+
pyramid,
101+
affine
102+
)
96103
return new PointGeometry(coordinates)
97104
} else if (type === 'POLYLINE') {
98105
const coordinates = data.map((d) => {
99-
return _scoord3dCoordinates2geometryCoordinates(d, pyramid)
106+
return _scoord3dCoordinates2geometryCoordinates(d, pyramid, affine)
100107
})
101108
return new LineStringGeometry(coordinates)
102109
} else if (type === 'POLYGON') {
103110
const coordinates = data.map((d) => {
104-
return _scoord3dCoordinates2geometryCoordinates(d, pyramid)
111+
return _scoord3dCoordinates2geometryCoordinates(d, pyramid, affine)
105112
})
106113
return new PolygonGeometry([coordinates])
107114
} else if (type === 'ELLIPSE') {
@@ -122,7 +129,7 @@ function _scoord3d2Geometry (scoord3d, pyramid) {
122129
point2
123130
]
124131
coordinates = coordinates.map((d) => {
125-
return _scoord3dCoordinates2geometryCoordinates(d, pyramid)
132+
return _scoord3dCoordinates2geometryCoordinates(d, pyramid, affine)
126133
})
127134
// to flat coordinates
128135
coordinates = [
@@ -177,29 +184,20 @@ function getPixelSpacing (metadata) {
177184
*
178185
* @param {array} coordinates - Array of Openlayers map coordinates
179186
* @param {object} pyramid - Metadata of images in the pyramid
187+
* @param {number[][]} affine - 3x3 affine transformation matrix
180188
* @returns {array} Array of slide coordinates
181189
* @private
182190
*/
183-
function _geometryCoordinates2scoord3dCoordinates (coordinates, pyramid) {
191+
function _geometryCoordinates2scoord3dCoordinates (
192+
coordinates,
193+
pyramid,
194+
affine
195+
) {
184196
let transform = false
185197
if (!Array.isArray(coordinates[0])) {
186198
coordinates = [coordinates]
187199
transform = true
188200
}
189-
const metadata = pyramid[pyramid.length - 1]
190-
const origin = metadata.TotalPixelMatrixOriginSequence[0]
191-
const orientation = metadata.ImageOrientationSlide
192-
const spacing = getPixelSpacing(metadata)
193-
const offset = [
194-
Number(origin.XOffsetInSlideCoordinateSystem),
195-
Number(origin.YOffsetInSlideCoordinateSystem)
196-
]
197-
198-
const affine = buildTransform({
199-
offset,
200-
orientation,
201-
spacing
202-
})
203201
coordinates = coordinates.map((c) => {
204202
const pixelCoord = [c[0], -(c[1] + 1)]
205203
const slideCoord = applyTransform({ coordinate: pixelCoord, affine })
@@ -216,30 +214,21 @@ function _geometryCoordinates2scoord3dCoordinates (coordinates, pyramid) {
216214
*
217215
* @param {array} coordinates - Array of slide coordinates
218216
* @param {object} pyramid - Metadata of images in the pyramid
217+
* @param {number[][]} affine - 3x3 affine transformation matrix
219218
* @returns {array} Array of Openlayers map coordinates
220219
* @private
221220
*/
222-
function _scoord3dCoordinates2geometryCoordinates (coordinates, pyramid) {
221+
function _scoord3dCoordinates2geometryCoordinates (
222+
coordinates,
223+
pyramid,
224+
affine
225+
) {
223226
let transform = false
224227
if (!Array.isArray(coordinates[0])) {
225228
coordinates = [coordinates]
226229
transform = true
227230
}
228-
const metadata = pyramid[pyramid.length - 1]
229-
const orientation = metadata.ImageOrientationSlide
230-
const spacing = getPixelSpacing(metadata)
231-
const origin = metadata.TotalPixelMatrixOriginSequence[0]
232-
const offset = [
233-
Number(origin.XOffsetInSlideCoordinateSystem),
234-
Number(origin.YOffsetInSlideCoordinateSystem)
235-
]
236-
237231
let outOfFrame = false
238-
const affine = buildInverseTransform({
239-
offset,
240-
orientation,
241-
spacing
242-
})
243232
coordinates = coordinates.map((c) => {
244233
if (c[0] > 25 || c[1] > 76) {
245234
outOfFrame = true
@@ -292,18 +281,19 @@ function _computeAreaOfPolygon (coordinates) {
292281
*
293282
* @param {Feature} feature - Openlayers feature
294283
* @param {object} pyramid - Metadata of images in the pyramid
284+
* @param {number[][]} affine - 3x3 affine transformation matrix
295285
* @returns {number} Length in millimeter
296286
* @private
297287
*/
298-
function _getFeatureLength (feature, pyramid) {
288+
function _getFeatureLength (feature, pyramid, affine) {
299289
const geometry = feature.getGeometry()
300290
const type = geometry.getType()
301291

302292
if (type === 'LineString') {
303293
const coordinates = geometry.getCoordinates()
304294
if (coordinates && coordinates.length) {
305295
const scoord3dCoordinates = coordinates.map((c) =>
306-
_geometryCoordinates2scoord3dCoordinates(c, pyramid)
296+
_geometryCoordinates2scoord3dCoordinates(c, pyramid, affine)
307297
)
308298
let length = 0
309299
for (let i = 0; i < (scoord3dCoordinates.length - 1); i++) {
@@ -327,10 +317,11 @@ function _getFeatureLength (feature, pyramid) {
327317
*
328318
* @param {Feature} feature - Openlayers feature
329319
* @param {object} pyramid - Metadata of images in the pyramid
320+
* @param {number[][]} affine - 3x3 affine transformation matrix
330321
* @returns {number} Area in square millimeter
331322
* @private
332323
*/
333-
function _getFeatureArea (feature, pyramid) {
324+
function _getFeatureArea (feature, pyramid, affine) {
334325
let geometry = feature.getGeometry()
335326
let type = geometry.getType()
336327

@@ -344,7 +335,9 @@ function _getFeatureArea (feature, pyramid) {
344335
if (coordinates && coordinates.length) {
345336
const scoord3dCoordinates = geometry
346337
.getCoordinates()[0]
347-
.map((c) => _geometryCoordinates2scoord3dCoordinates(c, pyramid))
338+
.map((c) => {
339+
return _geometryCoordinates2scoord3dCoordinates(c, pyramid, affine)
340+
})
348341
return _computeAreaOfPolygon(scoord3dCoordinates) * 1000
349342
}
350343
}

0 commit comments

Comments
 (0)