11import { getCompInit } from '#rx'
22import 'ol/ol.css'
3- import Map from 'ol/Map.js'
3+ import OLMap from 'ol/Map.js'
44import TileLayer from 'ol/layer/Tile.js'
55import Tile from 'ol/layer/Tile.js'
66import View from 'ol/View.js'
@@ -15,7 +15,6 @@ import wsiViewerDefaults from '#plots/wsiviewer/defaults.ts'
1515import type { WSImagesRequest , WSImagesResponse , WSImage } from '#types'
1616import wsiViewerImageFiles from './wsimagesloaded.ts'
1717import { table2col } from '#dom/table2col'
18-
1918export default class WSIViewer {
2019 // following attributes are required by rx
2120 private type : string
@@ -27,10 +26,16 @@ export default class WSIViewer {
2726
2827 private thumbnailsContainer : any
2928
29+ // Field to store sample ID to wsi session ID mapping
30+ private readonly wsiSessions : Map < string , string | undefined >
31+
3032 constructor ( opts : any ) {
3133 this . type = 'WSIViewer'
3234 this . opts = opts
3335 this . wsiViewerInteractions = new WSIViewerInteractions ( this , opts )
36+ this . wsiSessions = new Map ( )
37+ // Add event listener for tab/window close
38+ window . addEventListener ( 'beforeunload' , this . onTabClosed . bind ( this ) )
3439 }
3540
3641 async main ( ) : Promise < void > {
@@ -88,6 +93,61 @@ export default class WSIViewer {
8893 this . renderMetadata ( holder , layers , settings )
8994 }
9095
96+ private async getLayers ( state : any , wsimages : WSImage [ ] ) : Promise < Array < TileLayer < Zoomify > > > {
97+ const layers : Array < TileLayer < Zoomify > > = [ ]
98+
99+ for ( let i = 0 ; i < wsimages . length ; i ++ ) {
100+ const body : WSImagesRequest = {
101+ genome : state . genome || state . vocab . genome ,
102+ dslabel : state . dslabel || state . vocab . dslabel ,
103+ sampleId : state . sample_id ,
104+ wsimage : wsimages [ i ] . filename
105+ }
106+
107+ const data : WSImagesResponse = await dofetch3 ( 'wsimages' , { body } )
108+
109+ if ( data . status === 'error' ) {
110+ return [ ]
111+ }
112+
113+ this . wsiSessions . set ( wsimages [ i ] . filename , data . browserImageInstanceId )
114+
115+ const imgWidth = data . slide_dimensions [ 0 ]
116+ const imgHeight = data . slide_dimensions [ 1 ]
117+
118+ const zoomifyUrl = `/tileserver/layer/slide/${ data . wsiSessionId } /zoomify/{TileGroup}/{z}-{x}-{y}@1x.jpg`
119+
120+ const source = new Zoomify ( {
121+ url : zoomifyUrl ,
122+ size : [ imgWidth , imgHeight ] ,
123+ crossOrigin : 'anonymous' ,
124+ zDirection : - 1 // Ensure we get a tile with the screen resolution or higher
125+ } )
126+
127+ const options = {
128+ preview : `/tileserver/layer/slide/${ data . wsiSessionId } /zoomify/TileGroup0/0-0-0@1x.jpg` ,
129+ metadata : wsimages [ i ] . metadata ,
130+ source : source ,
131+ baseLayer : true
132+ }
133+
134+ const layer = new TileLayer ( options )
135+
136+ layers . push ( layer )
137+ }
138+ return layers
139+ }
140+
141+ private onTabClosed ( ) {
142+ const hasNonUndefinedValue = Array . from ( this . wsiSessions . values ( ) ) . some ( value => value !== undefined )
143+ if ( hasNonUndefinedValue ) {
144+ dofetch3 ( 'clearwsisession' , {
145+ method : 'DELETE' ,
146+ body : { sessions : JSON . stringify ( Array . from ( this . wsiSessions ) ) }
147+ } )
148+ }
149+ }
150+
91151 private generateThumbnails ( layers : Array < TileLayer < Zoomify > > , setting : Settings ) {
92152 if ( ! this . thumbnailsContainer ) {
93153 // First-time initialization
@@ -138,8 +198,8 @@ export default class WSIViewer {
138198 }
139199 }
140200
141- private getMap ( displayedImage : TileLayer ) {
142- return new Map ( {
201+ private getMap ( displayedImage : TileLayer ) : OLMap {
202+ return new OLMap ( {
143203 layers : [ displayedImage ] ,
144204 target : 'wsi-viewer' ,
145205 view : new View ( {
@@ -148,7 +208,7 @@ export default class WSIViewer {
148208 } )
149209 }
150210
151- private addControls ( map : Map , firstLayer : TileLayer ) {
211+ private addControls ( map : OLMap , firstLayer : TileLayer ) {
152212 const fullscreen = new FullScreen ( )
153213 map . addControl ( fullscreen )
154214
@@ -164,50 +224,6 @@ export default class WSIViewer {
164224 map . addControl ( overviewMapControl )
165225 }
166226
167- private async getLayers ( state : any , wsimages : WSImage [ ] ) : Promise < Array < TileLayer < Zoomify > > > {
168- const layers : Array < TileLayer < Zoomify > > = [ ]
169-
170- for ( let i = 0 ; i < wsimages . length ; i ++ ) {
171- const body : WSImagesRequest = {
172- genome : state . genome || state . vocab . genome ,
173- dslabel : state . dslabel || state . vocab . dslabel ,
174- sampleId : state . sample_id ,
175- wsimage : wsimages [ i ] . filename
176- }
177-
178- const data : WSImagesResponse = await dofetch3 ( 'wsimages' , { body } )
179-
180- if ( data . status === 'error' ) {
181- return [ ]
182- }
183-
184- const imgWidth = data . slide_dimensions [ 0 ]
185- const imgHeight = data . slide_dimensions [ 1 ]
186-
187- const zoomifyUrl = `/tileserver/layer/slide/${ data . sessionId } /zoomify/{TileGroup}/{z}-{x}-{y}@1x.jpg`
188-
189- const source = new Zoomify ( {
190- url : zoomifyUrl ,
191- size : [ imgWidth , imgHeight ] ,
192- crossOrigin : 'anonymous' ,
193- zDirection : - 1 // Ensure we get a tile with the screen resolution or higher
194- } )
195-
196- const options = {
197- // title: "Set Title",
198- preview : `/tileserver/layer/slide/${ data . sessionId } /zoomify/TileGroup0/0-0-0@1x.jpg` ,
199- metadata : wsimages [ i ] . metadata ,
200- source : source ,
201- baseLayer : true
202- }
203-
204- const layer = new TileLayer ( options )
205-
206- layers . push ( layer )
207- }
208- return layers
209- }
210-
211227 private renderMetadata ( holder : any , layers : Array < TileLayer < Zoomify > > , settings : Settings ) {
212228 holder . select ( 'div[id="metadata"]' ) . remove ( )
213229 const holderDiv = holder . append ( 'div' ) . attr ( 'id' , 'metadata' )
0 commit comments