1+ <!DOCTYPE html>
2+ < html lang ="en ">
3+
4+ < head >
5+ < meta charset ="UTF-8 ">
6+ < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
7+ < title > Loading...</ title >
8+ < link rel ="shortcut icon " type ="image/x-icon " href ="favicon.ico ">
9+ < script src ="https://cdn.plot.ly/plotly-2.35.3.min.js "> </ script >
10+ < script src ="https://unpkg.com/vue@3/dist/vue.global.prod.js "> </ script >
11+ < script src ="https://cdn.tailwindcss.com "> </ script >
12+ < style >
13+ /* Styling for the tooltip */
14+ .hover-tooltip {
15+ position : absolute;
16+ pointer-events : none;
17+ background : white;
18+ border : 1px solid # ccc ;
19+ padding : 10px ;
20+ border-radius : 5px ;
21+ box-shadow : 0 2px 5px rgba (0 , 0 , 0 , 0.2 );
22+ font-size : 16px ;
23+ font-weight : bold;
24+ display : none;
25+ }
26+
27+ .hover-tooltip img {
28+ max-width : 150px ;
29+ max-height : 150px ;
30+ display : block;
31+ margin : auto;
32+ }
33+
34+ .body {
35+ font-family : Arial, sans-serif;
36+ font-size : 16px ;
37+ color : black;
38+ font-weight : bold;
39+ }
40+ </ style >
41+ </ head >
42+
43+ < body class ="bg-gray-100 text-gray-900 font-sans ">
44+ < div id ="app ">
45+ < div class ="flex justify-center items-center mb-4 space-x-4 ">
46+ < label for ="batch-select " class ="font-semibold "> Select Compound Range:</ label >
47+ < select v-model ="selectedBatchIndex " id ="batch-select " class ="border p-2 rounded-md ">
48+ < option v-for ="(batch, index) in batchLabels " :key ="index " :value ="index "> {{ batch }}</ option >
49+ < option :value ="-1 "> Full Dataset</ option >
50+ </ select >
51+
52+ < label class ="flex items-center space-x-2 ">
53+ < input type ="checkbox " v-model ="showText " class ="form-checkbox h-5 w-5 text-blue-600 ">
54+ < span class ="font-semibold "> Show Text in Cells</ span >
55+ </ label >
56+ </ div >
57+
58+ < p v-if ="!heatmapData " class ="text-center text-lg font-semibold "> Loading heatmap...</ p >
59+ < div id ="heatmap " class ="w-full h-[60vh] "> </ div >
60+ < div id ="tooltip " class ="hover-tooltip "> </ div >
61+ </ div >
62+
63+ < script >
64+ const { createApp} = Vue ;
65+
66+ createApp ( {
67+ data ( ) {
68+ return {
69+ pageTitle : "Loading..." ,
70+ graphName : "Loading..." ,
71+ heatmapData : null ,
72+ yieldData : null ,
73+ compoundChunks : [ ] ,
74+ batchLabels : [ ] ,
75+ selectedBatchIndex : 0 ,
76+ showText : true ,
77+ } ;
78+ } ,
79+ watch : {
80+ selectedBatchIndex ( newIndex ) {
81+ this . renderHeatmap ( newIndex ) ;
82+ } ,
83+ showText ( ) {
84+ this . renderHeatmap ( this . selectedBatchIndex ) ;
85+ }
86+ } ,
87+ mounted ( ) {
88+ fetch ( 'data/heatmap/Robussness_compounds.json' )
89+ . then ( response => response . json ( ) )
90+ . then ( data => {
91+ this . heatmapData = data ;
92+ this . pageTitle = data . page_title ;
93+ this . graphName = data . graph_name ;
94+ document . title = this . pageTitle ;
95+ return fetch ( data . yield_data_path ) ; // Load yield data dynamically
96+ } )
97+ . then ( response => response . json ( ) )
98+ . then ( yieldData => {
99+ this . yieldData = yieldData . yields ; // Store yield values
100+ this . splitCompounds ( ) ; // Slice compounds into batches of 50
101+ this . renderHeatmap ( 0 ) ; // Load the first batch initially
102+ } )
103+ . catch ( error => console . error ( 'Error loading heatmap data:' , error ) ) ;
104+ } ,
105+ methods : {
106+ splitCompounds ( ) {
107+ const compounds = this . heatmapData . compounds ;
108+ const chunkSize = 50 ;
109+ for ( let i = 0 ; i < compounds . length ; i += chunkSize ) {
110+ let batch = compounds . slice ( i , i + chunkSize ) ;
111+ this . compoundChunks . push ( batch ) ;
112+ this . batchLabels . push ( `${ batch [ 0 ] } - ${ batch [ batch . length - 1 ] } ` ) ;
113+ }
114+ } ,
115+ renderHeatmap ( batchIndex ) {
116+ console . log ( "Entering renderHeatmap" ) ;
117+ if ( ! this . heatmapData || ! this . yieldData ) return ;
118+
119+ // compound and method lists
120+ const methods = this . heatmapData . methods ;
121+ const compoundBatch = batchIndex === - 1 ? this . heatmapData . compounds : this . compoundChunks [ batchIndex ] ;
122+ // Dynamically map yield values
123+ const z_values = methods . map ( method =>
124+ compoundBatch . map ( compound => this . yieldData [ compound ] ?. [ method ] ?? null )
125+ ) ;
126+ console . log ( methods , compoundBatch , z_values ) ;
127+
128+ const trace1 = {
129+ type : 'heatmap' ,
130+ z : z_values ,
131+ x : compoundBatch ,
132+ y : methods ,
133+ colorscale : 'Red' ,
134+ text : this . showText ? z_values : [ ] ,
135+ texttemplate : this . showText ? '%{text:.1f}' : '' ,
136+ hoverinfo : 'none' ,
137+ colorbar : {
138+ title : 'Yield (%)' ,
139+ titleside : 'right' ,
140+ borderwidth : 0 ,
141+ dtick : 10 ,
142+ len : 1 ,
143+ orientation : 'v' ,
144+ thickness : 20 ,
145+ } ,
146+ } ;
147+ const data = [ trace1 ] ;
148+ const fontDefault = {
149+ color : 'black' ,
150+ family : 'Courier New, monospace' ,
151+ size : 16
152+ } ;
153+ const layout = {
154+ title : {
155+ text : `<b>${ this . graphName } </b>` ,
156+ font : { ...fontDefault , size : 32 , family : 'Arial, sans-serif' } ,
157+ xref : 'paper' ,
158+ yref : 'paper' ,
159+ automargin : true ,
160+ } ,
161+ xaxis : {
162+ title : {
163+ text : "<b>" + "Compounds" + "</b>" ,
164+ font : { ...fontDefault , size : 24 , family : 'Arial, sans-serif' } ,
165+ xref : 'paper' ,
166+ yref : 'paper' ,
167+ automargin : true ,
168+ } ,
169+ scaleanchor : 'y' , // Ensures 1:1 ratio
170+ scaleratio : 1 ,
171+ font : fontDefault ,
172+ ticks : 'outside' ,
173+ tickangle : "auto" ,
174+ } ,
175+ yaxis : {
176+ title : {
177+ text : "<b>" + "Methods" + "</b>" ,
178+ font : { ...fontDefault , size : 24 , family : 'Arial, sans-serif' } ,
179+ xref : 'paper' ,
180+ yref : 'paper' ,
181+ automargin : true ,
182+ } ,
183+ font : fontDefault ,
184+ ticks : 'outside' ,
185+ tickangle : "auto" ,
186+ } ,
187+ margin : {
188+ l : 100 , r : 50 , t : 50 , b : 100
189+ } ,
190+ autosize : true ,
191+ } ;
192+ const config = { responsive : true } ;
193+
194+ Plotly . react ( 'heatmap' , data , layout , config ) ;
195+
196+ this . setupTooltip ( ) ;
197+ } ,
198+ setupTooltip ( ) {
199+ const tooltip = document . getElementById ( 'tooltip' ) ;
200+ const heatmap = document . getElementById ( 'heatmap' ) ;
201+
202+ heatmap . on ( 'plotly_hover' , ( event ) => {
203+ const point = event . points [ 0 ] ;
204+ const compoundId = point . x ;
205+ const method = point . y ;
206+ const yieldValue = point . z ;
207+ // determine the cell size
208+ var cellSize = Math . abs ( point . bbox . width || ( point . bbox . x1 - point . bbox . x0 ) ) ;
209+ // Tooltip content
210+ const imgSrc = this . heatmapData . images [ compoundId ] ;
211+ tooltip . innerHTML = `
212+ <img src="${ imgSrc } " alt="Image for ${ compoundId } ">
213+ <div class="text-sm"><b>Compound:</b> ${ compoundId } </div>
214+ <div class="text-sm"><b>Method:</b> ${ method } </div>
215+ <div class="text-sm"><b>Yield:</b> ${ yieldValue !== null && yieldValue !== undefined ? yieldValue . toFixed ( 2 ) + "%" : "N/A" } </div>
216+ ` ;
217+
218+ // Get the bounding box of the hovered cell
219+ const bbox = point . bbox || { x0 : event . event . clientX , y0 : event . event . clientY } ;
220+ const cellWidth = Math . abs ( bbox . x1 - bbox . x0 ) ;
221+ const cellHeight = Math . abs ( bbox . y1 - bbox . y0 ) ;
222+ // Position the tooltip relative to the hovered cell
223+ tooltip . style . left = `${ bbox . x0 + cellWidth / 4 * 3 } px` ;
224+ tooltip . style . top = `${ bbox . y0 + cellHeight / 4 * 3 } px` ;
225+ tooltip . style . display = 'block' ;
226+ } ) ;
227+
228+ heatmap . on ( 'plotly_unhover' , ( ) => {
229+ tooltip . style . display = 'none' ;
230+ } ) ;
231+ }
232+ }
233+ } ) . mount ( '#app' ) ;
234+ </ script >
235+ </ body >
236+
237+ </ html >
0 commit comments