@@ -42,25 +42,157 @@ import {
4242import DICOMwebClient from 'dicomweb-client/build/dicomweb-client.js'
4343
4444
45- const _client = Symbol ( 'client' ) ;
46- const _controls = Symbol ( 'controls' ) ;
47- const _coordinateFormatScoord3d2Geometry = Symbol ( 'coordinateFormatScoord3d2Geometry' ) ;
48- const _coordinateFormatGeometry2Scoord3d = Symbol ( 'coordinateFormatGeometry2Scoord3d' ) ;
45+ function _geometry2Scoord3d ( geometry , pyramid ) {
46+ const type = geometry . getType ( ) ;
47+ if ( type === 'Point' ) {
48+ let coordinates = geometry . getCoordinates ( ) ;
49+ coordinates = _geometryCoordinates2scoord3dCoordinates ( coordinates , pyramid ) ;
50+ return new Point ( {
51+ coordinates,
52+ referencedFrameOfReferenceUID : pyramid [ pyramid . length - 1 ] . frameOfReferenceUID
53+ } ) ;
54+ } else if ( type === 'Polygon' ) {
55+ /*
56+ * The first linear ring of the array defines the outer-boundary (surface).
57+ * Each subsequent linear ring defines a hole in the surface.
58+ */
59+ let coordinates = geometry . getCoordinates ( ) [ 0 ] . map ( c => {
60+ return _geometryCoordinates2scoord3dCoordinates ( c , pyramid ) ;
61+ } ) ;
62+ return new Polygon ( {
63+ coordinates,
64+ referencedFrameOfReferenceUID : pyramid [ pyramid . length - 1 ] . frameOfReferenceUID
65+ } ) ;
66+ } else if ( type === 'LineString' ) {
67+ let coordinates = geometry . getCoordinates ( ) . map ( c => {
68+ return _geometryCoordinates2scoord3dCoordinates ( c , pyramid ) ;
69+ } ) ;
70+ return new Polyline ( {
71+ coordinates,
72+ referencedFrameOfReferenceUID : pyramid [ pyramid . length - 1 ] . frameOfReferenceUID
73+ } ) ;
74+ } else if ( type === 'Circle' ) {
75+ // chunking the Flat Coordinates into two arrays within 3 elements each
76+ let coordinates = geometry . getFlatCoordinates ( ) . reduce ( ( all , one , i ) => {
77+ const ch = Math . floor ( i / 2 )
78+ all [ ch ] = [ ] . concat ( ( all [ ch ] || [ ] ) , one )
79+ return all
80+ } , [ ] )
81+ coordinates = coordinates . map ( c => {
82+ c . push ( 0 )
83+ return _geometryCoordinates2scoord3dCoordinates ( c , pyramid )
84+ } )
85+ return new Circle ( {
86+ coordinates,
87+ referencedFrameOfReferenceUID : pyramid [ pyramid . length - 1 ] . frameOfReferenceUID
88+ } ) ;
89+ } else {
90+ // TODO: Combine multiple points into MULTIPOINT.
91+ console . error ( `unknown geometry type "${ type } "` )
92+ }
93+ }
94+
95+ function _scoord3d2Geometry ( scoord3d , pyramid ) {
96+ const type = scoord3d . graphicType ;
97+ const data = scoord3d . graphicData ;
98+ if ( type === 'POINT' ) {
99+ let coordinates = _scoord3dCoordinates2geometryCoordinates ( data , pyramid ) ;
100+ return new PointGeometry ( coordinates ) ;
101+ } else if ( type === 'POLYLINE' ) {
102+ const coordinates = data . map ( d => {
103+ return _scoord3dCoordinates2geometryCoordinates ( d , pyramid ) ;
104+ } ) ;
105+ return new LineStringGeometry ( coordinates ) ;
106+ } else if ( type === 'POLYGON' ) {
107+ const coordinates = data . map ( d => {
108+ return _scoord3dCoordinates2geometryCoordinates ( d , pyramid ) ;
109+ } ) ;
110+ return new PolygonGeometry ( [ coordinates ] ) ;
111+ } else if ( type === 'CIRCLE' ) {
112+ let coordinates = data . map ( d => {
113+ return _scoord3dCoordinates2geometryCoordinates ( d , pyramid ) ;
114+ } )
115+ // to flat coordinates
116+ coordinates = [ ...coordinates [ 0 ] . slice ( 0 , 2 ) , ...coordinates [ 1 ] . slice ( 0 , 2 ) ]
117+
118+ // flat coordinates in combination with opt_layout and no opt_radius are also accepted
119+ // and internaly it calculates the Radius
120+ return new CircleGeometry ( coordinates , null , "XY" ) ;
121+ } else {
122+ console . error ( `unsupported graphic type "${ type } "` )
123+ }
124+ }
125+
126+ function _geometryCoordinates2scoord3dCoordinates ( coordinates , pyramid ) {
127+ return _coordinateFormatGeometry2Scoord3d ( [ coordinates [ 0 ] + 1 , - coordinates [ 1 ] , coordinates [ 2 ] ] , pyramid ) ;
128+ }
129+
130+ function _scoord3dCoordinates2geometryCoordinates ( coordinates , pyramid ) {
131+ return _coordinateFormatScoord3d2Geometry ( [ coordinates [ 0 ] , coordinates [ 1 ] , coordinates [ 2 ] ] , pyramid )
132+ }
133+
134+ /*
135+ * Translate pixel units of total pixel matrix into millimeters of
136+ * slide coordinate system
137+ */
138+ function _coordinateFormatGeometry2Scoord3d ( coordinates , pyramid ) {
139+ if ( coordinates . length === 3 ) {
140+ coordinates = [ coordinates ] ;
141+ }
142+ coordinates . map ( coord => {
143+ let x = ( coord [ 0 ] * pyramid [ pyramid . length - 1 ] . pixelSpacing [ 0 ] ) . toFixed ( 4 ) ;
144+ let y = ( - ( coord [ 1 ] - 1 ) * pyramid [ pyramid . length - 1 ] . pixelSpacing [ 1 ] ) . toFixed ( 4 ) ;
145+ let z = ( 1 ) . toFixed ( 4 ) ;
146+ coordinates = [ Number ( x ) , Number ( y ) , Number ( z ) ] ;
147+ } )
148+ return ( coordinates ) ;
149+ }
150+
151+ /*
152+ * Translate millimeters into pixel units of total pixel matrix of
153+ * slide coordinate system
154+ */
155+ function _coordinateFormatScoord3d2Geometry ( coordinates , pyramid ) {
156+ if ( coordinates . length === 3 ) {
157+ coordinates = [ coordinates ] ;
158+ }
159+ coordinates . map ( coord => {
160+ let x = ( coord [ 0 ] / pyramid [ pyramid . length - 1 ] . pixelSpacing [ 0 ] - 1 ) ;
161+ let y = ( coord [ 1 ] / pyramid [ pyramid . length - 1 ] . pixelSpacing [ 1 ] - 1 ) ;
162+ let z = coord [ 2 ] ;
163+ coordinates = [ x , y , z ] ;
164+ } ) ;
165+ return ( coordinates ) ;
166+ }
167+
168+ function _getROIFromFeature ( feature , pyramid ) {
169+ let roi = { }
170+ if ( feature !== undefined ) {
171+ const geometry = feature . getGeometry ( ) ;
172+ const scoord3d = _geometry2Scoord3d ( geometry , pyramid ) ;
173+ const properties = feature . getProperties ( ) ;
174+ // Remove geometry from properties mapping
175+ const geometryName = feature . getGeometryName ( ) ;
176+ delete properties [ geometryName ] ;
177+ const uid = feature . getId ( ) ;
178+ roi = new ROI ( { scoord3d, properties, uid} ) ;
179+ }
180+ return roi ;
181+ }
182+
183+ const _usewebgl = Symbol ( 'usewebgl' ) ;
184+ const _map = Symbol ( 'map' ) ;
185+ const _features = Symbol ( 'features' ) ;
49186const _drawingSource = Symbol ( 'drawingSource' ) ;
50187const _drawingLayer = Symbol ( 'drawingLayer' ) ;
51- const _features = Symbol ( 'features' ) ;
52- const _geometryCoordinates2scoord3dCoordinates = Symbol ( 'geometryCoordinates2scoord3dCoordinates' ) ;
53- const _geometry2Scoord3d = Symbol ( 'geometry2Scoord3d' ) ;
54- const _getROIFromFeature = Symbol ( 'getROIFromFeature' ) ;
55- const _interactions = Symbol ( 'interactions' ) ;
56- const _map = Symbol ( 'map' ) ;
57- const _metadata = Symbol ( 'metadata' ) ;
188+ const _segmentations = Symbol ( 'segmentations' ) ;
58189const _pyramid = Symbol ( 'pyramid' ) ;
190+ const _client = Symbol ( 'client' ) ;
191+ const _controls = Symbol ( 'controls' ) ;
192+ const _interactions = Symbol ( 'interactions' ) ;
59193const _pyramidBase = Symbol ( 'pyramidBaseLayer' ) ;
60- const _scoord3d2Geometry = Symbol ( 'scoord3d2Geometry' ) ;
61- const _scoord3dCoordinates2geometryCoordinates = Symbol ( 'scoord3dCoordinates2geometryCoordinates' ) ;
62- const _segmentations = Symbol ( 'segmentations' ) ;
63- const _usewebgl = Symbol ( 'usewebgl' ) ;
194+ const _metadata = Symbol ( 'metadata' ) ;
195+
64196
65197class VLWholeSlideMicroscopyImageViewer {
66198
@@ -522,18 +654,17 @@ class VLWholeSlideMicroscopyImageViewer {
522654 scaleInnerElement . style . willChange = 'contents,width' ;
523655
524656 const container = this [ _map ] . getTargetElement ( ) ;
525- const getROIFromFeature = this [ _getROIFromFeature ] . bind ( this ) ;
526657
527658 this [ _drawingSource ] . on ( VectorEventType . ADDFEATURE , ( e ) => {
528- publish ( container , EVENT . ROI_ADDED , getROIFromFeature ( e . feature ) ) ;
659+ publish ( container , EVENT . ROI_ADDED , _getROIFromFeature ( e . feature , this . _pyramid ) ) ;
529660 } ) ;
530661
531662 this [ _drawingSource ] . on ( VectorEventType . CHANGEFEATURE , ( e ) => {
532- publish ( container , EVENT . ROI_MODIFIED , getROIFromFeature ( e . feature ) ) ;
663+ publish ( container , EVENT . ROI_MODIFIED , _getROIFromFeature ( e . feature , this . _pyramid ) ) ;
533664 } ) ;
534665
535666 this [ _drawingSource ] . on ( VectorEventType . REMOVEFEATURE , ( e ) => {
536- publish ( container , EVENT . ROI_REMOVED , getROIFromFeature ( e . feature ) ) ;
667+ publish ( container , EVENT . ROI_REMOVED , _getROIFromFeature ( e . feature , this . _pyramid ) ) ;
537668 } ) ;
538669
539670 this [ _map ] . on ( MapEventType . MOVESTART , ( e ) => {
@@ -602,12 +733,11 @@ class VLWholeSlideMicroscopyImageViewer {
602733 this [ _interactions ] . draw = new Draw ( allDrawOptions ) ;
603734
604735 const container = this [ _map ] . getTargetElement ( ) ;
605- const getROIFromFeature = this [ _getROIFromFeature ] . bind ( this ) ;
606736
607737 //attaching openlayers events handling
608738 this [ _interactions ] . draw . on ( 'drawend' , ( e ) => {
609739 e . feature . setId ( generateUuid ( ) ) ;
610- publish ( container , EVENT . ROI_DRAWN , getROIFromFeature ( e . feature ) ) ;
740+ publish ( container , EVENT . ROI_DRAWN , _getROIFromFeature ( e . feature , this . _pyramid ) ) ;
611741 } ) ;
612742
613743 this [ _map ] . addInteraction ( this [ _interactions ] . draw ) ;
@@ -638,7 +768,7 @@ class VLWholeSlideMicroscopyImageViewer {
638768 const container = this [ _map ] . getTargetElement ( ) ;
639769
640770 this [ _interactions ] . select . on ( 'select' , ( e ) => {
641- publish ( container , EVENT . ROI_SELECTED , _getROIFromFeature ( e . selected [ 0 ] ) ) ;
771+ publish ( container , EVENT . ROI_SELECTED , _getROIFromFeature ( e . selected [ 0 ] , this . _pyramid ) ) ;
642772 } ) ;
643773
644774 this [ _map ] . addInteraction ( this [ _interactions ] . select ) ;
@@ -696,7 +826,7 @@ class VLWholeSlideMicroscopyImageViewer {
696826
697827 getROI ( index ) {
698828 const feature = this [ _features ] . item ( index ) ;
699- return this [ _getROIFromFeature ] ( feature ) ;
829+ return _getROIFromFeature ( feature , this . _pyramid ) ;
700830 }
701831
702832 indexOfROI ( item ) {
@@ -710,18 +840,18 @@ class VLWholeSlideMicroscopyImageViewer {
710840
711841 popROI ( ) {
712842 const feature = this [ _features ] . pop ( ) ;
713- return this [ _getROIFromFeature ] ( feature ) ;
843+ return _getROIFromFeature ( feature , this . _pyramid ) ;
714844 }
715845
716846 addROI ( item ) {
717- const geometry = this [ _scoord3d2Geometry ] ( item . scoord3d , this . _pyramid ) ;
847+ const geometry = _scoord3d2Geometry ( item . scoord3d , this . _pyramid ) ;
718848 const feature = new Feature ( geometry ) ;
719849 feature . setProperties ( item . properties , true ) ;
720850 this [ _features ] . push ( feature ) ;
721851 }
722852
723853 updateROI ( index , item ) {
724- const geometry = this [ _scoord3d2Geometry ] ( item . scoord3d , this . _pyramid ) ;
854+ const geometry = _scoord3d2Geometry ( item . scoord3d , this . _pyramid ) ;
725855 const feature = new Feature ( geometry ) ;
726856 feature . setProperties ( item . properties , true ) ;
727857 feature . setId ( item . uid ) ;
@@ -743,144 +873,6 @@ class VLWholeSlideMicroscopyImageViewer {
743873 get areROIsVisible ( ) {
744874 return this [ _drawingLayer ] . getVisible ( ) ;
745875 }
746-
747- [ _geometry2Scoord3d ] ( geometry ) {
748- const type = geometry . getType ( ) ;
749- if ( type === 'Point' ) {
750- let coordinates = geometry . getCoordinates ( ) ;
751- coordinates = this [ _geometryCoordinates2scoord3dCoordinates ] ( coordinates ) ;
752- return new Point ( {
753- coordinates,
754- referencedFrameOfReferenceUID : this [ _pyramidBase ] . frameOfReferenceUID
755- } ) ;
756- } else if ( type === 'Polygon' ) {
757- /*
758- * The first linear ring of the array defines the outer-boundary (surface).
759- * Each subsequent linear ring defines a hole in the surface.
760- */
761- let coordinates = geometry . getCoordinates ( ) [ 0 ] . map ( c => {
762- return this [ _geometryCoordinates2scoord3dCoordinates ] ( c ) ;
763- } ) ;
764- return new Polygon ( {
765- coordinates,
766- referencedFrameOfReferenceUID : this [ _pyramidBase ] . frameOfReferenceUID
767- } ) ;
768- } else if ( type === 'LineString' ) {
769- let coordinates = geometry . getCoordinates ( ) . map ( c => {
770- return this [ _geometryCoordinates2scoord3dCoordinates ] ( c ) ;
771- } ) ;
772- return new Polyline ( {
773- coordinates,
774- referencedFrameOfReferenceUID : this [ _pyramidBase ] . frameOfReferenceUID
775- } ) ;
776- } else if ( type === 'Circle' ) {
777- // chunking the Flat Coordinates into two arrays within 3 elements each
778- let coordinates = geometry . getFlatCoordinates ( ) . reduce ( ( all , one , i ) => {
779- const ch = Math . floor ( i / 2 )
780- all [ ch ] = [ ] . concat ( ( all [ ch ] || [ ] ) , one )
781- return all
782- } , [ ] )
783- coordinates = coordinates . map ( c => {
784- c . push ( 0 )
785- return this [ _geometryCoordinates2scoord3dCoordinates ] ( c )
786- } )
787- return new Circle ( {
788- coordinates,
789- referencedFrameOfReferenceUID : this [ _pyramidBase ] . frameOfReferenceUID
790- } ) ;
791- } else {
792- // TODO: Combine multiple points into MULTIPOINT.
793- console . error ( `unknown geometry type "${ type } "` )
794- }
795- }
796-
797- [ _scoord3d2Geometry ] ( scoord3d ) {
798- const type = scoord3d . graphicType ;
799- const data = scoord3d . graphicData ;
800- if ( type === 'POINT' ) {
801- let coordinates = this [ _scoord3dCoordinates2geometryCoordinates ] ( data ) ;
802- return new PointGeometry ( { coordinates} ) ;
803- } else if ( type === 'POLYLINE' ) {
804- const coordinates = data . map ( d => {
805- return this [ _scoord3dCoordinates2geometryCoordinates ] ( d ) ;
806- } ) ;
807- return new LineStringGeometry ( { coordinates} ) ;
808- } else if ( type === 'POLYGON' ) {
809- const coordinates = data . map ( d => {
810- return this [ _scoord3dCoordinates2geometryCoordinates ] ( d ) ;
811- } ) ;
812- return new PolygonGeometry ( [ coordinates ] ) ;
813- } else if ( type === 'CIRCLE' ) {
814- let coordinates = data . map ( d => {
815- return this [ _scoord3dCoordinates2geometryCoordinates ] ( d ) ;
816- } )
817- // to flat coordinates
818- coordinates = [ ...coordinates [ 0 ] . slice ( 0 , 2 ) , ...coordinates [ 1 ] . slice ( 0 , 2 ) ]
819-
820- // flat coordinates in combination with opt_layout and no opt_radius are also accepted
821- // and internaly it calculates the Radius
822- return new CircleGeometry ( coordinates , null , "XY" ) ;
823- } else {
824- console . error ( `unsupported graphic type "${ type } "` )
825- }
826- }
827-
828- [ _geometryCoordinates2scoord3dCoordinates ] ( coordinates ) {
829- return this [ _coordinateFormatGeometry2Scoord3d ] ( [ coordinates [ 0 ] + 1 , - coordinates [ 1 ] , coordinates [ 2 ] ] ) ;
830- }
831-
832- [ _scoord3dCoordinates2geometryCoordinates ] ( coordinates ) {
833- return this [ _coordinateFormatScoord3d2Geometry ] ( [ coordinates [ 0 ] , coordinates [ 1 ] , coordinates [ 2 ] ] )
834- }
835-
836- /*
837- * Translate pixel units of total pixel matrix into millimeters of
838- * slide coordinate system
839- */
840- [ _coordinateFormatGeometry2Scoord3d ] ( coordinates ) {
841- if ( coordinates . length === 3 ) {
842- coordinates = [ coordinates ] ;
843- }
844- coordinates . map ( coord => {
845- let x = ( coord [ 0 ] * this [ _pyramidBase ] . pixelSpacing [ 0 ] ) . toFixed ( 4 ) ;
846- let y = ( - ( coord [ 1 ] - 1 ) * this [ _pyramidBase ] . pixelSpacing [ 1 ] ) . toFixed ( 4 ) ;
847- let z = ( 1 ) . toFixed ( 4 ) ;
848- coordinates = [ Number ( x ) , Number ( y ) , Number ( z ) ] ;
849- } )
850- return ( coordinates ) ;
851- }
852-
853- /*
854- * Translate millimeters into pixel units of total pixel matrix of
855- * slide coordinate system
856- */
857- [ _coordinateFormatScoord3d2Geometry ] ( coordinates ) {
858- if ( coordinates . length === 3 ) {
859- coordinates = [ coordinates ] ;
860- }
861- coordinates . map ( coord => {
862- let x = ( coord [ 0 ] / this . _pyramid [ pyramid . length - 1 ] . pixelSpacing [ 0 ] - 1 ) ;
863- let y = ( coord [ 1 ] / this . _pyramid [ pyramid . length - 1 ] . pixelSpacing [ 1 ] - 1 ) ;
864- let z = coord [ 2 ] ;
865- coordinates = [ x , y , z ] ;
866- } ) ;
867- return ( coordinates ) ;
868- }
869-
870- [ _getROIFromFeature ] ( feature ) {
871- let roi = { }
872- if ( feature !== undefined ) {
873- const geometry = feature . getGeometry ( ) ;
874- const scoord3d = this [ _geometry2Scoord3d ] ( geometry ) ;
875- const properties = feature . getProperties ( ) ;
876- // Remove geometry from properties mapping
877- const geometryName = feature . getGeometryName ( ) ;
878- delete properties [ geometryName ] ;
879- const uid = feature . getId ( ) ;
880- roi = new ROI ( { scoord3d, properties, uid} ) ;
881- }
882- return roi ;
883- }
884876}
885877
886878export { VLWholeSlideMicroscopyImageViewer } ;
0 commit comments