Skip to content

Commit 5dea96a

Browse files
authored
Improve configuration (#75)
* Fix handling of annotation group load errors * Enable configuration of tile preload * Disable overview if low-resolution image is large * Fix vulnerabilities * Make overview map collapsible
1 parent a5781bf commit 5dea96a

File tree

3 files changed

+119
-85
lines changed

3 files changed

+119
-85
lines changed

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dicom-microscopy-viewer",
3-
"version": "0.36.4",
3+
"version": "0.36.5",
44
"description": "Interactive web-based viewer for DICOM Microscopy Images",
55
"main": "./src/dicom-microscopy-viewer.js",
66
"standard": {

src/viewer.js

Lines changed: 110 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,7 @@ class VolumeImageViewer {
797797
* @param {metadata.VLWholeSlideMicroscopyImage[]} options.metadata -
798798
* Metadata of DICOM VL Whole Slide Microscopy Image instances that should be
799799
* diplayed.
800+
* @param {Object} options.preload - Whether data should be preloaded
800801
* @param {string[]} [options.controls=[]] - Names of viewer control elements
801802
* that should be included in the viewport
802803
* @param {boolean} [options.debug=false] - Whether debug features should be
@@ -809,6 +810,14 @@ class VolumeImageViewer {
809810

810811
if (this[_options].debug == null) {
811812
this[_options].debug = false
813+
} else {
814+
this[_options].debug = true
815+
}
816+
817+
if (this[_options].preload == null) {
818+
this[_options].preload = false
819+
} else {
820+
this[_options].preload = true
812821
}
813822

814823
if (this[_options].tilesCacheSize == null) {
@@ -1145,7 +1154,7 @@ class VolumeImageViewer {
11451154
opticalPath.layer = new TileLayer({
11461155
source,
11471156
extent: pyramid.extent,
1148-
preload: 1,
1157+
preload: this[_options].preload ? 1 : 0,
11491158
style: style,
11501159
visible: false,
11511160
useInterimTilesOnError: false,
@@ -1227,7 +1236,6 @@ class VolumeImageViewer {
12271236
projection: this[_projection],
12281237
wrapX: false,
12291238
transition: 0,
1230-
preload: 1,
12311239
bandCount: 3
12321240
})
12331241
source.on('tileloaderror', (event) => {
@@ -1248,7 +1256,7 @@ class VolumeImageViewer {
12481256
opticalPath.layer = new TileLayer({
12491257
source,
12501258
extent: this[_tileGrid].extent,
1251-
preload: 1,
1259+
preload: this[_options].preload ? 1 : 0,
12521260
useInterimTilesOnError: false,
12531261
cacheSize: this[_options].tilesCacheSize
12541262
})
@@ -1288,24 +1296,28 @@ class VolumeImageViewer {
12881296
layers.push(tileDebugLayer)
12891297
}
12901298

1291-
const overviewView = new View({
1292-
projection: this[_projection],
1293-
rotation: this[_rotation],
1294-
constrainOnlyCenter: false,
1295-
smoothExtentConstraint: true,
1296-
smoothResolutionConstraint: true,
1297-
minZoom: 0,
1298-
maxZoom: 0,
1299-
extent: this[_pyramid].extent
1300-
})
1299+
if (Math.max(...this[_pyramid].gridSizes[0]) <= 10) {
1300+
const overviewView = new View({
1301+
projection: this[_projection],
1302+
rotation: this[_rotation],
1303+
constrainOnlyCenter: false,
1304+
smoothExtentConstraint: true,
1305+
smoothResolutionConstraint: true,
1306+
minZoom: 0,
1307+
maxZoom: 0,
1308+
extent: this[_pyramid].extent
1309+
})
13011310

1302-
this[_overviewMap] = new OverviewMap({
1303-
view: overviewView,
1304-
layers: overviewLayers,
1305-
collapsed: false,
1306-
collapsible: false,
1307-
rotateWithView: true
1308-
})
1311+
this[_overviewMap] = new OverviewMap({
1312+
view: overviewView,
1313+
layers: overviewLayers,
1314+
collapsed: false,
1315+
collapsible: true,
1316+
rotateWithView: true
1317+
})
1318+
} else {
1319+
this[_overviewMap] = null
1320+
}
13091321

13101322
this[_drawingSource] = new VectorSource({
13111323
tileGrid: this[_tileGrid],
@@ -1376,7 +1388,9 @@ class VolumeImageViewer {
13761388
}
13771389

13781390
if (this[_options].controls.has('overview')) {
1379-
this[_controls].overview = this[_overviewMap]
1391+
if (this[_overviewMap]) {
1392+
this[_controls].overview = this[_overviewMap]
1393+
}
13801394
}
13811395

13821396
for (const control in this[_controls]) {
@@ -1578,10 +1592,12 @@ class VolumeImageViewer {
15781592
0,
15791593
opticalPath.layer
15801594
)
1581-
this[_overviewMap].getOverviewMap().getLayers().insertAt(
1582-
0,
1583-
opticalPath.overviewTileLayer
1584-
)
1595+
if (this[_overviewMap]) {
1596+
this[_overviewMap].getOverviewMap().getLayers().insertAt(
1597+
0,
1598+
opticalPath.overviewTileLayer
1599+
)
1600+
}
15851601
}
15861602

15871603
/**
@@ -1601,9 +1617,11 @@ class VolumeImageViewer {
16011617
return
16021618
}
16031619
this[_map].removeLayer(opticalPath.layer)
1604-
this[_overviewMap].getOverviewMap().removeLayer(
1605-
opticalPath.overviewTileLayer
1606-
)
1620+
if (this[_overviewMap]) {
1621+
this[_overviewMap].getOverviewMap().removeLayer(
1622+
opticalPath.overviewTileLayer
1623+
)
1624+
}
16071625
}
16081626

16091627
/**
@@ -1843,43 +1861,53 @@ class VolumeImageViewer {
18431861
scaleInnerElement.style.margin = '1px'
18441862
scaleInnerElement.style.willChange = 'contents,width'
18451863

1846-
const overviewElement = this[_overviewMap].element
1847-
const overviewmapElement = Object.values(overviewElement.children).find(
1848-
c => c.className === 'ol-overviewmap-map'
1849-
)
1850-
overviewmapElement.style.border = '1px solid black'
1851-
// Try to fit the overview map into the target control overlay container
1852-
const height = Math.abs(this[_pyramid].extent[1])
1853-
const width = Math.abs(this[_pyramid].extent[2])
1854-
const rotation = this[_rotation] / Math.PI * 180
1855-
const windowSize = _getWindowSize()
1856-
let targetHeight
1857-
let resizeFactor
1858-
let targetWidth
1859-
if (Math.abs(rotation - 180) < 0.01 || Math.abs(rotation - 0) < 0.01) {
1860-
if (windowSize[1] > windowSize[0]) {
1861-
targetHeight = Math.ceil(windowSize[1] * 0.2)
1862-
resizeFactor = targetHeight / height
1863-
targetWidth = width * resizeFactor
1864-
} else {
1865-
targetWidth = Math.ceil(windowSize[0] * 0.15)
1866-
resizeFactor = targetWidth / width
1867-
targetHeight = height * resizeFactor
1864+
if (this[_overviewMap]) {
1865+
const overviewElement = this[_overviewMap].element
1866+
const overviewmapElement = Object.values(overviewElement.children).find(
1867+
c => c.className === 'ol-overviewmap-map'
1868+
)
1869+
const buttonElement = Object.values(overviewElement.children).find(
1870+
c => c.type === 'button'
1871+
)
1872+
if (buttonElement) {
1873+
buttonElement.style.border = '0.25px solid black'
1874+
buttonElement.style.backgroundColor = 'white'
18681875
}
1869-
} else {
1870-
if (windowSize[1] > windowSize[0]) {
1871-
targetHeight = Math.ceil(windowSize[1] * 0.2)
1872-
resizeFactor = targetHeight / width
1873-
targetWidth = height * resizeFactor
1876+
overviewmapElement.style.border = '1px solid black'
1877+
overviewmapElement.style.color = 'black'
1878+
// Try to fit the overview map into the target control overlay container
1879+
const height = Math.abs(this[_pyramid].extent[1])
1880+
const width = Math.abs(this[_pyramid].extent[2])
1881+
const rotation = this[_rotation] / Math.PI * 180
1882+
const windowSize = _getWindowSize()
1883+
let targetHeight
1884+
let resizeFactor
1885+
let targetWidth
1886+
if (Math.abs(rotation - 180) < 0.01 || Math.abs(rotation - 0) < 0.01) {
1887+
if (windowSize[1] > windowSize[0]) {
1888+
targetHeight = Math.ceil(windowSize[1] * 0.2)
1889+
resizeFactor = targetHeight / height
1890+
targetWidth = width * resizeFactor
1891+
} else {
1892+
targetWidth = Math.ceil(windowSize[0] * 0.15)
1893+
resizeFactor = targetWidth / width
1894+
targetHeight = height * resizeFactor
1895+
}
18741896
} else {
1875-
targetWidth = Math.ceil(windowSize[0] * 0.15)
1876-
resizeFactor = targetWidth / height
1877-
targetHeight = width * resizeFactor
1897+
if (windowSize[1] > windowSize[0]) {
1898+
targetHeight = Math.ceil(windowSize[1] * 0.2)
1899+
resizeFactor = targetHeight / width
1900+
targetWidth = height * resizeFactor
1901+
} else {
1902+
targetWidth = Math.ceil(windowSize[0] * 0.15)
1903+
resizeFactor = targetWidth / height
1904+
targetHeight = width * resizeFactor
1905+
}
18781906
}
1907+
overviewmapElement.style.width = `${targetWidth}px`
1908+
overviewmapElement.style.height = `${targetHeight}px`
1909+
this[_overviewMap].getOverviewMap().updateSize()
18791910
}
1880-
overviewmapElement.style.width = `${targetWidth}px`
1881-
overviewmapElement.style.height = `${targetHeight}px`
1882-
this[_overviewMap].getOverviewMap().updateSize()
18831911
})
18841912
}
18851913

@@ -2128,17 +2156,19 @@ class VolumeImageViewer {
21282156
* @returns {void}
21292157
*/
21302158
toggleOverviewMap () {
2131-
const controls = this[_map].getControls()
2132-
const overview = controls.getArray().find((c) => c === this[_overviewMap])
2133-
if (overview) {
2134-
this[_map].removeControl(this[_overviewMap])
2135-
return
2159+
if (this[_overviewMap]) {
2160+
const controls = this[_map].getControls()
2161+
const overview = controls.getArray().find((c) => c === this[_overviewMap])
2162+
if (overview) {
2163+
this[_map].removeControl(this[_overviewMap])
2164+
return
2165+
}
2166+
this[_map].addControl(this[_overviewMap])
2167+
const map = this[_overviewMap].getOverviewMap()
2168+
const view = map.getView()
2169+
const projection = view.getProjection()
2170+
view.fit(projection.getExtent(), { size: map.getSize() })
21362171
}
2137-
this[_map].addControl(this[_overviewMap])
2138-
const map = this[_overviewMap].getOverviewMap()
2139-
const view = map.getView()
2140-
const projection = view.getProjection()
2141-
view.fit(projection.getExtent(), { size: map.getSize() })
21422172
}
21432173

21442174
/**
@@ -2392,11 +2422,15 @@ class VolumeImageViewer {
23922422
}
23932423

23942424
collapseOverviewMap () {
2395-
this[_overviewMap].setCollapsed(true)
2425+
if (this[_overviewMap]) {
2426+
this[_overviewMap].setCollapsed(true)
2427+
}
23962428
}
23972429

23982430
expandOverviewMap () {
2399-
this[_overviewMap].setCollapsed(true)
2431+
if (this[_overviewMap]) {
2432+
this[_overviewMap].setCollapsed(true)
2433+
}
24002434
}
24012435

24022436
/**
@@ -2830,7 +2864,7 @@ class VolumeImageViewer {
28302864
const container = this[_map].getTargetElement()
28312865
publish(container, EVENT.LOADING_ENDED)
28322866
})
2833-
source.on('featureserror', (event) => {
2867+
source.on('featuresloaderror', (event) => {
28342868
const container = this[_map].getTargetElement()
28352869
publish(container, EVENT.LOADING_ENDED)
28362870
})
@@ -3352,7 +3386,7 @@ class VolumeImageViewer {
33523386
extent: this[_pyramid].extent,
33533387
visible: false,
33543388
opacity: 0.9,
3355-
preload: 0,
3389+
preload: this[_options].preload ? 1 : 0,
33563390
transition: 0,
33573391
style: _getColorPaletteStyleForTileLayer({
33583392
windowCenter,
@@ -3783,7 +3817,7 @@ class VolumeImageViewer {
37833817
projection: this[_projection],
37843818
visible: false,
37853819
opacity: 1,
3786-
preload: 1,
3820+
preload: this[_options].preload ? 1 : 0,
37873821
transition: 0,
37883822
style: _getColorPaletteStyleForTileLayer({
37893823
windowCenter,

0 commit comments

Comments
 (0)