Skip to content

Commit f259754

Browse files
committed
Add new function to add map overlays
1 parent 2bbd1ef commit f259754

File tree

1 file changed

+50
-39
lines changed

1 file changed

+50
-39
lines changed

src/viewer.js

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import publish from "./eventPublisher";
1414
import ScaleLine from 'ol/control/ScaleLine';
1515
import Select from 'ol/interaction/Select';
1616
import Static from 'ol/source/ImageStatic';
17+
import Overlay from 'ol/Overlay';
1718
import TileLayer from 'ol/layer/Tile';
1819
import TileImage from 'ol/source/TileImage';
1920
import TileGrid from 'ol/tilegrid/TileGrid';
@@ -34,9 +35,9 @@ import { toStringXY, rotate } from 'ol/coordinate';
3435
import { VLWholeSlideMicroscopyImage, getFrameMapping } from './metadata.js';
3536
import { ROI } from './roi.js';
3637
import {
37-
generateUID,
38-
mapPixelCoordToSlideCoord,
39-
mapSlideCoordToPixelCoord
38+
generateUID,
39+
mapPixelCoordToSlideCoord,
40+
mapSlideCoordToPixelCoord
4041
} from './utils.js';
4142
import {
4243
Point,
@@ -47,8 +48,7 @@ import {
4748
Ellipse
4849
} from './scoord3d.js';
4950

50-
import DICOMwebClient from 'dicomweb-client/build/dicomweb-client.js'
51-
51+
import * as DICOMwebClient from 'dicomweb-client';
5252

5353
/** Extracts value of Pixel Spacing attribute from metadata.
5454
*
@@ -173,7 +173,7 @@ function _getRotation(metadata) {
173173
*/
174174
function _geometry2Scoord3d(geometry, pyramid) {
175175
console.info('map coordinates from pixel matrix to slide coordinate system')
176-
const frameOfReferenceUID = pyramid[pyramid.length-1].FrameOfReferenceUID;
176+
const frameOfReferenceUID = pyramid[pyramid.length - 1].FrameOfReferenceUID;
177177
const type = geometry.getType();
178178
if (type === 'Point') {
179179
let coordinates = geometry.getCoordinates();
@@ -213,7 +213,7 @@ function _geometry2Scoord3d(geometry, pyramid) {
213213
[center[0], center[1] + radius, 0],
214214
];
215215
coordinates = coordinates.map(c => {
216-
return _geometryCoordinates2scoord3dCoordinates(c, pyramid);
216+
return _geometryCoordinates2scoord3dCoordinates(c, pyramid);
217217
})
218218

219219
// const metadata = pyramid[pyramid.length-1];
@@ -250,7 +250,7 @@ function _scoord3d2Geometry(scoord3d, pyramid) {
250250
return _scoord3dCoordinates2geometryCoordinates(d, pyramid);
251251
});
252252
return new LineStringGeometry(coordinates);
253-
} else if(type === 'POLYGON'){
253+
} else if (type === 'POLYGON') {
254254
const coordinates = data.map(d => {
255255
return _scoord3dCoordinates2geometryCoordinates(d, pyramid);
256256
});
@@ -276,7 +276,7 @@ function _scoord3d2Geometry(scoord3d, pyramid) {
276276
return _scoord3dCoordinates2geometryCoordinates(d, pyramid);
277277
});
278278
// to flat coordinates
279-
coordinates = [...coordinates[0].slice(0,2), ...coordinates[1].slice(0,2)];
279+
coordinates = [...coordinates[0].slice(0, 2), ...coordinates[1].slice(0, 2)];
280280

281281
// flat coordinates in combination with opt_layout and no opt_radius are also accepted
282282
// and internaly it calculates the Radius
@@ -304,11 +304,11 @@ function _scoord3dCoordinates2geometryCoordinates(coordinates, pyramid) {
304304
*/
305305
function _coordinateFormatGeometry2Scoord3d(coordinates, pyramid) {
306306
let transform = false;
307-
if(!Array.isArray(coordinates[0])) {
307+
if (!Array.isArray(coordinates[0])) {
308308
coordinates = [coordinates];
309309
transform = true;
310310
}
311-
const metadata = pyramid[pyramid.length-1];
311+
const metadata = pyramid[pyramid.length - 1];
312312
const origin = metadata.TotalPixelMatrixOriginSequence[0];
313313
const orientation = metadata.ImageOrientationSlide;
314314
const spacing = _getPixelSpacing(metadata);
@@ -344,11 +344,11 @@ function _coordinateFormatGeometry2Scoord3d(coordinates, pyramid) {
344344
*/
345345
function _coordinateFormatScoord3d2Geometry(coordinates, pyramid) {
346346
let transform = false;
347-
if(!Array.isArray(coordinates[0])) {
347+
if (!Array.isArray(coordinates[0])) {
348348
coordinates = [coordinates];
349349
transform = true;
350350
}
351-
const metadata = pyramid[pyramid.length-1];
351+
const metadata = pyramid[pyramid.length - 1];
352352
const orientation = metadata.ImageOrientationSlide;
353353
const spacing = _getPixelSpacing(metadata);
354354
const origin = metadata.TotalPixelMatrixOriginSequence[0];
@@ -357,7 +357,7 @@ function _coordinateFormatScoord3d2Geometry(coordinates, pyramid) {
357357
Number(origin.YOffsetInSlideCoordinateSystem),
358358
];
359359

360-
coordinates = coordinates.map(c =>{
360+
coordinates = coordinates.map(c => {
361361
const slideCoord = [c[0], c[1]];
362362
const pixelCoord = mapSlideCoordToPixelCoord({
363363
offset,
@@ -381,7 +381,7 @@ function _coordinateFormatScoord3d2Geometry(coordinates, pyramid) {
381381
* @returns {ROI} Region of interest
382382
* @private
383383
*/
384-
function _getROIFromFeature(feature, pyramid){
384+
function _getROIFromFeature(feature, pyramid) {
385385
let roi = {}
386386
if (feature !== undefined) {
387387
const geometry = feature.getGeometry();
@@ -391,7 +391,7 @@ function _getROIFromFeature(feature, pyramid){
391391
const geometryName = feature.getGeometryName();
392392
delete properties[geometryName];
393393
const uid = feature.getId();
394-
roi = new ROI({scoord3d, properties, uid});
394+
roi = new ROI({ scoord3d, properties, uid });
395395
}
396396
return roi;
397397
}
@@ -452,7 +452,7 @@ class VolumeImageViewer {
452452
this[_segmentations] = {};
453453

454454
// Collection of Openlayers "Feature" instances
455-
this[_features] = new Collection([], {unique: true});
455+
this[_features] = new Collection([], { unique: true });
456456
// Add unique identifier to each created "Feature" instance
457457
this[_features].on('add', (e) => {
458458
// The ID may have already been set when drawn. However, features could
@@ -469,7 +469,7 @@ class VolumeImageViewer {
469469
*/
470470
this[_metadata] = [];
471471
options.metadata.forEach(m => {
472-
const image = new VLWholeSlideMicroscopyImage({metadata: m});
472+
const image = new VLWholeSlideMicroscopyImage({ metadata: m });
473473
if (image.ImageType[2] === 'VOLUME') {
474474
this[_metadata].push(image);
475475
}
@@ -503,9 +503,9 @@ class VolumeImageViewer {
503503
let index = null;
504504
for (let j = 0; j < this[_pyramidMetadata].length; j++) {
505505
if (
506-
(this[_pyramidMetadata][j].TotalPixelMatrixColumns === cols) &&
507-
(this[_pyramidMetadata][j].TotalPixelMatrixRows === rows)
508-
) {
506+
(this[_pyramidMetadata][j].TotalPixelMatrixColumns === cols) &&
507+
(this[_pyramidMetadata][j].TotalPixelMatrixRows === rows)
508+
) {
509509
alreadyExists = true;
510510
index = j;
511511
}
@@ -617,7 +617,7 @@ class VolumeImageViewer {
617617
const path = pyramidFrameMappings[z][index];
618618
if (path === undefined) {
619619
console.warn("tile " + index + " not found at level " + z);
620-
return(null);
620+
return (null);
621621
}
622622
let url = options.client.wadoURL +
623623
"/studies/" + pyramid[z].StudyInstanceUID +
@@ -626,7 +626,7 @@ class VolumeImageViewer {
626626
if (options.retrieveRendered) {
627627
url = url + '/rendered';
628628
}
629-
return(url);
629+
return (url);
630630
}
631631

632632
/*
@@ -653,7 +653,7 @@ class VolumeImageViewer {
653653
}
654654
};
655655
options.client.retrieveInstanceFramesRendered(retrieveOptions).then((renderedFrame) => {
656-
const blob = new Blob([renderedFrame], {type: mediaType});
656+
const blob = new Blob([renderedFrame], { type: mediaType });
657657
img.src = window.URL.createObjectURL(blob);
658658
});
659659
} else {
@@ -665,10 +665,10 @@ class VolumeImageViewer {
665665
seriesInstanceUID,
666666
sopInstanceUID,
667667
frameNumbers,
668-
mediaTypes: [{mediaType, transferSyntaxUID: '1.2.840.10008.1.2.4.50'}]
668+
mediaTypes: [{ mediaType, transferSyntaxUID: '1.2.840.10008.1.2.4.50' }]
669669
};
670670
options.client.retrieveInstanceFrames(retrieveOptions).then((rawFrames) => {
671-
const blob = new Blob(rawFrames, {type: mediaType});
671+
const blob = new Blob(rawFrames, { type: mediaType });
672672
img.src = window.URL.createObjectURL(blob);
673673
});
674674
}
@@ -706,7 +706,7 @@ class VolumeImageViewer {
706706
/** DICOM Pixel Spacing has millimeter unit while the projection has
707707
* has meter unit.
708708
*/
709-
const spacing = _getPixelSpacing(pyramid[nLevels-1])[0] / 10**3;
709+
const spacing = _getPixelSpacing(pyramid[nLevels - 1])[0] / 10 ** 3;
710710
return pixelRes * spacing;
711711
}
712712
});
@@ -817,6 +817,7 @@ class VolumeImageViewer {
817817
controls: [],
818818
keyboardEventTarget: document,
819819
});
820+
820821
this[_map].addInteraction(new MouseWheelZoom());
821822

822823
for (let control in this[_controls]) {
@@ -826,15 +827,15 @@ class VolumeImageViewer {
826827
}
827828

828829
/** Resizes the viewer to fit the viewport. */
829-
resize(){
830+
resize() {
830831
this[_map].updateSize();
831832
}
832833

833834
/** Gets the size of the viewport.
834835
*
835836
* @type {number[]}
836837
*/
837-
get size(){
838+
get size() {
838839
return this[_map].getSize();
839840
}
840841

@@ -895,9 +896,9 @@ class VolumeImageViewer {
895896
const layout = geometry.getLayout();
896897
const coordinates = geometry.getCoordinates();
897898
const firstPoint = coordinates[0][0];
898-
const lastPoint = coordinates[0][coordinates[0].length-1];
899+
const lastPoint = coordinates[0][coordinates[0].length - 1];
899900
if (firstPoint[0] !== lastPoint[0] || firstPoint[1] !== lastPoint[1]) {
900-
coordinates[0][coordinates[0].length-1] = firstPoint;
901+
coordinates[0][coordinates[0].length - 1] = firstPoint;
901902
geometry.setCoordinates(coordinates, layout);
902903
e.feature.setGeometry(geometry);
903904
}
@@ -961,7 +962,7 @@ class VolumeImageViewer {
961962
console.error(`unsupported geometry type "${options.geometryType}"`)
962963
}
963964

964-
const defaultDrawOptions = {source: this[_drawingSource]};
965+
const defaultDrawOptions = { source: this[_drawingSource] };
965966
const customDrawOptions = customOptionsMapping[options.geometryType];
966967
if ('style' in options) {
967968
customDrawOptions.style = options.style;
@@ -1002,7 +1003,7 @@ class VolumeImageViewer {
10021003
*
10031004
* @param {Object} options - Selection options.
10041005
*/
1005-
activateSelectInteraction(options={}) {
1006+
activateSelectInteraction(options = {}) {
10061007
this.deactivateSelectInteraction();
10071008
console.info('activate "select" interaction')
10081009
this[_interactions].select = new Select({
@@ -1039,7 +1040,7 @@ class VolumeImageViewer {
10391040
*
10401041
* @param {Object} options - Modification options.
10411042
*/
1042-
activateModifyInteraction(options={}) {
1043+
activateModifyInteraction(options = {}) {
10431044
this.deactivateModifyInteraction();
10441045
console.info('activate "modify" interaction')
10451046
this[_interactions].modify = new Modify({
@@ -1073,7 +1074,7 @@ class VolumeImageViewer {
10731074
console.info('get all ROIs')
10741075
let rois = [];
10751076
this[_features].forEach((item) => {
1076-
rois.push(this.getROI(item.getId()));
1077+
rois.push(this.getROI(item.getId()));
10771078
});
10781079
return rois;
10791080
}
@@ -1120,6 +1121,16 @@ class VolumeImageViewer {
11201121
this[_features].push(feature);
11211122
}
11221123

1124+
/** Adds a new viewport overlay.
1125+
*
1126+
* @param {object} options Overlay options
1127+
* @param {object} options.element The custom overlay html element
1128+
* @param {object} options.className Class to style the OpenLayer's overlay container
1129+
*/
1130+
addViewportOverlay({ element, className }) {
1131+
this[_map].addOverlay(new Overlay({ element, className }));
1132+
}
1133+
11231134
/** Removes an individual regions of interest.
11241135
*
11251136
* @param {string} uid - Unique identifier of the region of interest
@@ -1227,7 +1238,7 @@ class _NonVolumeImageViewer {
12271238
}
12281239
};
12291240
options.client.retrieveInstanceRendered(retrieveOptions).then((thumbnail) => {
1230-
const blob = new Blob([thumbnail], {type: mediaType});
1241+
const blob = new Blob([thumbnail], { type: mediaType });
12311242
image.getImage().src = window.URL.createObjectURL(blob);
12321243
});
12331244
}
@@ -1241,7 +1252,7 @@ class _NonVolumeImageViewer {
12411252
* has meter unit.
12421253
*/
12431254
const mmSpacing = _getPixelSpacing(this[_metadata])[0];
1244-
const spacing = (mmSpacing / resizeFactor) / 10**3;
1255+
const spacing = (mmSpacing / resizeFactor) / 10 ** 3;
12451256
return pixelRes * spacing;
12461257
}
12471258
});
@@ -1304,15 +1315,15 @@ class _NonVolumeImageViewer {
13041315
}
13051316

13061317
/** Resizes the viewer to fit the viewport. */
1307-
resize(){
1318+
resize() {
13081319
this[_map].updateSize();
13091320
}
13101321

13111322
/** Gets the size of the viewport.
13121323
*
13131324
* @type {number[]}
13141325
*/
1315-
get size(){
1326+
get size() {
13161327
return this[_map].getSize();
13171328
}
13181329

0 commit comments

Comments
 (0)