davanstrien HF Staff commited on
Commit
95cefed
·
verified ·
1 Parent(s): f5e4692

explorer: clickable legend filter (toggle relationship types in map + ego views)

Browse files
Files changed (1) hide show
  1. app.py +47 -13
app.py CHANGED
@@ -240,10 +240,15 @@ td.bar span{display:block;height:11px;background:var(--bar);opacity:.85}
240
  td.num{text-align:right;color:var(--muted);font-variant-numeric:tabular-nums;width:48px}
241
  #wrap{display:flex;height:calc(100vh - 132px);position:relative}
242
  #net{flex:2.3;min-width:0}
243
- #legend{position:absolute;left:14px;bottom:14px;background:rgba(255,255,248,.92);
244
- border:1px solid var(--rule);padding:8px 11px;font-size:11px;line-height:1.7;max-width:200px;
245
- box-shadow:0 1px 3px rgba(0,0,0,.06)}
246
- #legend .lt{white-space:nowrap}
 
 
 
 
 
247
  @media(max-width:760px){#legend{display:none}}
248
  #side{flex:1;max-width:440px;overflow:auto;padding:22px 28px;border-left:1px solid var(--rule)}
249
  #side h2{font-weight:400;font-size:19px;margin:0;word-break:break-all}
@@ -289,13 +294,40 @@ const COLORS={exact_copy:'#8a8a80',filtered_subset:'#4a6b8a',subset_modified:'#5
289
  subset_reformatted:'#39597a',cleaned:'#6a8a5a',regenerated_variant:'#b08a3e',
290
  modified_variant:'#9a8a6a',augmentation:'#7a6a9a',augmentation_modified:'#8a7aaa',
291
  translation:'#a8566a',reformat:'#4a8a8a',partial_overlap:'#aaaaa0',declared:'#c8c8b8'};
292
- // corner colour key
293
- (function(){let h='<div style="font-style:italic;color:var(--muted);margin-bottom:3px">relationship</div>';
294
- for(const t of ['exact_copy','filtered_subset','subset_modified','subset_reformatted','cleaned',
295
- 'regenerated_variant','augmentation','augmentation_modified','translation','reformat','modified_variant','partial_overlap'])
296
- h+='<div class="lt"><span class="dot" style="background:'+COLORS[t]+'"></span>'+t.replace(/_/g,' ')+'</div>';
297
- h+='<div class="lt"><span class="dot" style="background:'+COLORS.declared+'"></span>declared (dashed)</div>';
 
 
 
 
 
298
  document.getElementById('legend').innerHTML=h;})();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
300
  // ---------- Overview ----------
301
  function bars(rows,opts){
@@ -342,7 +374,7 @@ async function map(){
342
  shape:'dot',size:6+Math.sqrt(n.derivatives||1)*2.4,
343
  color:{background:'#43586b',border:'#43586b'},
344
  font:{color:'#111',face:'Palatino, Georgia, serif',size:13,strokeWidth:4,strokeColor:'#fffff8'}}));
345
- const edges=g.edges.map(e=>({from:e.from,to:e.to,arrows:{to:{scaleFactor:.4}},
346
  color:{color:COLORS[e.type]||'#999',opacity:.9},width:1.6,title:e.type+' · '+e.confidence}));
347
  network=new vis.Network(document.getElementById('net'),{nodes,edges},
348
  {physics:{stabilization:{iterations:160,updateInterval:50},
@@ -350,6 +382,7 @@ async function map(){
350
  nodes:{borderWidth:0},edges:{smooth:false},interaction:{hover:true,hideEdgesOnDrag:true,tooltipDelay:120}});
351
  network.on('stabilizationIterationsDone',()=>network.setOptions({physics:false}));
352
  network.on('click',p=>{if(p.nodes.length)showExplorer(p.nodes[0]);});
 
353
  document.getElementById('side').innerHTML='<h2>Lineage map</h2><div class="meta">'+
354
  g.nodes.length.toLocaleString()+' source datasets · '+g.edges.length+' multi-level chain links</div>'+
355
  '<p class="row">Node <b>size</b> = number of derivatives. Edge <b>colour</b> = relationship type (key, lower-left). '+
@@ -363,11 +396,11 @@ async function go(){
363
  fetch('/related?dataset='+encodeURIComponent(ds)).then(x=>x.json())]);
364
  const top=new Set(g.nodes.slice().sort((a,b)=>(b.downloads||0)-(a.downloads||0)).slice(0,40).map(n=>n.id));
365
  const big=g.nodes.length>250;
366
- const nodes=g.nodes.map(n=>({id:n.id,label:(n.focus||top.has(n.id)||!big)?n.label:undefined,title:n.title,shape:'dot',
367
  size:n.focus?16:(6+Math.log10((n.downloads||0)+10)*2.2),
368
  color:{background:n.focus?'#c1440e':'#43586b',border:n.focus?'#c1440e':'#43586b'},
369
  font:{color:'#111',face:'Palatino, Georgia, serif',size:n.focus?16:12,strokeWidth:4,strokeColor:'#fffff8'}}));
370
- const edges=g.edges.map(e=>({from:e.from,to:e.to,arrows:{to:{scaleFactor:.45}},
371
  color:{color:COLORS[e.type]||'#999',opacity:.8},width:e.source==='inferred'?1.2:.6,
372
  dashes:e.source==='declared',title:e.type+' · conf '+e.confidence+' · '+e.source}));
373
  // adaptive layout: hub-shaped (many children of focus) -> force (spreads radially);
@@ -387,6 +420,7 @@ async function go(){
387
  network=new vis.Network(document.getElementById('net'),{nodes,edges},opts);
388
  if(useForce)network.on('stabilizationIterationsDone',()=>network.setOptions({physics:false}));
389
  network.on('click',p=>{if(p.nodes.length){showExplorer(p.nodes[0]);}});
 
390
  renderSide(r);
391
  }
392
  function lst(title,arr){
 
240
  td.num{text-align:right;color:var(--muted);font-variant-numeric:tabular-nums;width:48px}
241
  #wrap{display:flex;height:calc(100vh - 132px);position:relative}
242
  #net{flex:2.3;min-width:0}
243
+ #legend{position:absolute;left:14px;bottom:14px;background:rgba(255,255,248,.94);
244
+ border:1px solid var(--rule);padding:8px 11px;font-size:11px;line-height:1.75;max-width:210px;
245
+ box-shadow:0 1px 3px rgba(0,0,0,.06);user-select:none}
246
+ #legend .lt{white-space:nowrap;cursor:pointer;padding:1px 4px;margin:0 -4px;border-radius:3px}
247
+ #legend .lt:hover{background:#f1f1e6}
248
+ #legend .lt.off{opacity:.35;text-decoration:line-through}
249
+ #legend .reset{margin-top:5px;padding-top:4px;border-top:1px solid var(--rule);
250
+ font-style:italic;color:var(--muted);cursor:pointer}
251
+ #legend .reset:hover{color:var(--ink)}
252
  @media(max-width:760px){#legend{display:none}}
253
  #side{flex:1;max-width:440px;overflow:auto;padding:22px 28px;border-left:1px solid var(--rule)}
254
  #side h2{font-weight:400;font-size:19px;margin:0;word-break:break-all}
 
294
  subset_reformatted:'#39597a',cleaned:'#6a8a5a',regenerated_variant:'#b08a3e',
295
  modified_variant:'#9a8a6a',augmentation:'#7a6a9a',augmentation_modified:'#8a7aaa',
296
  translation:'#a8566a',reformat:'#4a8a8a',partial_overlap:'#aaaaa0',declared:'#c8c8b8'};
297
+ // corner colour key — clickable filter (toggle type visibility)
298
+ const LEGEND_TYPES=['exact_copy','filtered_subset','subset_modified','subset_reformatted','cleaned',
299
+ 'regenerated_variant','augmentation','augmentation_modified','translation','reformat','modified_variant',
300
+ 'partial_overlap','combined','declared'];
301
+ const ACTIVE_TYPES=new Set(LEGEND_TYPES);
302
+ let currentNetwork=null;
303
+ (function(){let h='<div style="font-style:italic;color:var(--muted);margin-bottom:3px">relationship · click to filter</div>';
304
+ for(const t of LEGEND_TYPES)
305
+ h+='<div class="lt" data-type="'+t+'"><span class="dot" style="background:'+(COLORS[t]||'#999')+'"></span>'+
306
+ t.replace(/_/g,' ')+(t==='declared'?' (dashed)':'')+'</div>';
307
+ h+='<div class="reset" data-reset="1">reset all</div>';
308
  document.getElementById('legend').innerHTML=h;})();
309
+ function applyFilter(){
310
+ if(!currentNetwork)return;
311
+ const edgesDS=currentNetwork.body.data.edges, nodesDS=currentNetwork.body.data.nodes;
312
+ const allEdges=edgesDS.get();
313
+ edgesDS.update(allEdges.map(e=>({id:e.id, hidden:!ACTIVE_TYPES.has(e.__type)})));
314
+ // hide nodes that have NO visible incident edge (except the focus)
315
+ const visible=new Set();
316
+ for(const e of allEdges) if(ACTIVE_TYPES.has(e.__type)){visible.add(e.from);visible.add(e.to);}
317
+ nodesDS.update(nodesDS.get().map(n=>({id:n.id, hidden:!visible.has(n.id) && !n.__focus})));
318
+ }
319
+ document.getElementById('legend').addEventListener('click',ev=>{
320
+ const reset=ev.target.closest('[data-reset]');
321
+ if(reset){LEGEND_TYPES.forEach(t=>ACTIVE_TYPES.add(t));
322
+ document.querySelectorAll('#legend .lt').forEach(el=>el.classList.remove('off'));
323
+ applyFilter();return;}
324
+ const lt=ev.target.closest('.lt');
325
+ if(!lt||!lt.dataset.type)return;
326
+ const t=lt.dataset.type;
327
+ if(ACTIVE_TYPES.has(t)){ACTIVE_TYPES.delete(t);lt.classList.add('off');}
328
+ else{ACTIVE_TYPES.add(t);lt.classList.remove('off');}
329
+ applyFilter();
330
+ });
331
 
332
  // ---------- Overview ----------
333
  function bars(rows,opts){
 
374
  shape:'dot',size:6+Math.sqrt(n.derivatives||1)*2.4,
375
  color:{background:'#43586b',border:'#43586b'},
376
  font:{color:'#111',face:'Palatino, Georgia, serif',size:13,strokeWidth:4,strokeColor:'#fffff8'}}));
377
+ const edges=g.edges.map(e=>({from:e.from,to:e.to,__type:e.type,arrows:{to:{scaleFactor:.4}},
378
  color:{color:COLORS[e.type]||'#999',opacity:.9},width:1.6,title:e.type+' · '+e.confidence}));
379
  network=new vis.Network(document.getElementById('net'),{nodes,edges},
380
  {physics:{stabilization:{iterations:160,updateInterval:50},
 
382
  nodes:{borderWidth:0},edges:{smooth:false},interaction:{hover:true,hideEdgesOnDrag:true,tooltipDelay:120}});
383
  network.on('stabilizationIterationsDone',()=>network.setOptions({physics:false}));
384
  network.on('click',p=>{if(p.nodes.length)showExplorer(p.nodes[0]);});
385
+ currentNetwork=network;applyFilter();
386
  document.getElementById('side').innerHTML='<h2>Lineage map</h2><div class="meta">'+
387
  g.nodes.length.toLocaleString()+' source datasets · '+g.edges.length+' multi-level chain links</div>'+
388
  '<p class="row">Node <b>size</b> = number of derivatives. Edge <b>colour</b> = relationship type (key, lower-left). '+
 
396
  fetch('/related?dataset='+encodeURIComponent(ds)).then(x=>x.json())]);
397
  const top=new Set(g.nodes.slice().sort((a,b)=>(b.downloads||0)-(a.downloads||0)).slice(0,40).map(n=>n.id));
398
  const big=g.nodes.length>250;
399
+ const nodes=g.nodes.map(n=>({id:n.id,__focus:n.focus,label:(n.focus||top.has(n.id)||!big)?n.label:undefined,title:n.title,shape:'dot',
400
  size:n.focus?16:(6+Math.log10((n.downloads||0)+10)*2.2),
401
  color:{background:n.focus?'#c1440e':'#43586b',border:n.focus?'#c1440e':'#43586b'},
402
  font:{color:'#111',face:'Palatino, Georgia, serif',size:n.focus?16:12,strokeWidth:4,strokeColor:'#fffff8'}}));
403
+ const edges=g.edges.map(e=>({from:e.from,to:e.to,__type:e.type,arrows:{to:{scaleFactor:.45}},
404
  color:{color:COLORS[e.type]||'#999',opacity:.8},width:e.source==='inferred'?1.2:.6,
405
  dashes:e.source==='declared',title:e.type+' · conf '+e.confidence+' · '+e.source}));
406
  // adaptive layout: hub-shaped (many children of focus) -> force (spreads radially);
 
420
  network=new vis.Network(document.getElementById('net'),{nodes,edges},opts);
421
  if(useForce)network.on('stabilizationIterationsDone',()=>network.setOptions({physics:false}));
422
  network.on('click',p=>{if(p.nodes.length){showExplorer(p.nodes[0]);}});
423
+ currentNetwork=network;applyFilter();
424
  renderSide(r);
425
  }
426
  function lst(title,arr){