Commit 0e57010d authored by Romain Loth's avatar Romain Loth

facet options editor modal in GUI

parent e8867b90
......@@ -60,6 +60,7 @@
<link type="text/css" href="twitterAPI2/twitterlibs/tweet.light.ltr.css" rel="stylesheet"/>
<link type="text/css" href="twitterAPI2/twitterlibs/custom.css" rel="stylesheet"/>
<link type="text/css" href="libs/css2/user_form.css" rel="stylesheet"/>
<!-- JS -->
<!-- <script src="script.js"></script> -->
......@@ -330,8 +331,8 @@
</a>
</li>
<li>
<a href="#savemodal" id="saveAs" class="zoombarbuttons" data-toggle="modal">
<li>
<a href="#savemodal" id="saveAs" class="zoombarbuttons" data-toggle="modal" data-target="#savemodal">
<img style="width:28px" title="Save As..." src="libs/img2/save.png"></img>
</a>
</li>
......@@ -366,7 +367,14 @@
<li>
<a href="#" id="noverlapButton" class="zoombarbuttons"
title="Disperse Overlapping Nodes">
<img src="libs/img2/disperse.png" style="width:36px">
<img src="libs/img2/disperse.png" style="width:32px">
</a>
</li>
<li>
<a href="#facet-options" id="facets" class="zoombarbuttons" data-toggle="modal" data-target="#facet-options">
<img style="width:32px" title="Facets coloring options" src="libs/img2/facet_options.png"></img>
</a>
</li>
......@@ -578,6 +586,91 @@
</div>
<div id="facet-options" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Facet options</h4>
</div>
<div class="modal-body">
<div class="uform-white">
<form id="facet_options_form" onsubmit="console.info('submitted')">
<h3 class="formcat">Edit attribute:</h3><br/>
<div class="question">
<div class="input-group">
<label for="choose-attr" class="smlabel input-group-addon">Attribute</label>
<select id="choose-attr" name="choose-attr"
class="custom-select form-control">
<option selected value="0"></option>
<!-- filled by fillAttrsInForm() -->
</select>
</div>
</div>
<div class="question">
<div class="input-group">
<label for="attr-col" class="smlabel input-group-addon">Coloring function</label>
<select id="attr-col" name="attr-col"
class="custom-select form-control">
<option selected value="0"></option>
<option value="cluster">cluster</option>
<option value="gradient">gradient</option>
<option value="heatmap">heatmap</option>
</select>
</div>
</div>
<div class="question">
<div class="input-group">
<label for="attr-binmode" class="smlabel input-group-addon">Binning Mode</label>
<select id="attr-binmode" name="attr-binmode"
class="custom-select form-control" onchange="binmodeOpenNBins()">
<option selected value="0"></option>
<option value="off">No binning</option>
<option value="samerange">Same range between ticks</option>
<option value="samepop">Same population in each bin</option>
</select>
</div>
</div>
<div class="question conditional-q" id="attr-nbins-div">
<div class="input-group">
<label for="attr-nbins" class="smlabel input-group-addon">Number of bins</label>
<input id="attr-nbins" name="attr-nbins" maxlength="2"
type="text" class="form-control autocomp" placeholder="number of bins"
onchange="let castint = parseInt(this.value) ; if (castint != this.value || castint < 2) {this.value = 2} else if(castint > 24) {this.value = 24}"
>
</div>
<p class="legend">Please input an integer value between 2 and 24.</p>
</div>
<div class="question input-group">
<label for="attr-translation" class="smlabel input-group-addon"> Title </label>
<input id="attr-translation" name="attr-translation" maxlength="70"
type="text" class="form-control autocomp" placeholder="a text for the color menu"
>
</div>
</form>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal">
Cancel
</button>
<button class="btn btn-primary"
type=button onclick="newAttrConf()" data-dismiss="modal">
Ok
</button>
</div>
</div>
</div>
</div>
<div id="geomapmodal" class="modal fade">
<!-- <div class="modal-content"> -->
......@@ -641,8 +734,8 @@
<script src="settings_explorerjs.js" type="text/javascript" language="javascript"></script>
<script src="tinawebJS/enviroment.js" type="text/javascript" language="javascript"></script>
<script src="tinawebJS/sigma.parseCustom.js" type="text/javascript" language="javascript"></script>
<script src="extras_explorerjs.js" type="text/javascript" language="javascript"></script>
<script src="tinawebJS/sigmaUtils.js" type="text/javascript" language="javascript"></script>
<script src="extras_explorerjs.js" type="text/javascript" language="javascript"></script>
<script src="tinawebJS/methods.js" type="text/javascript" language="javascript"></script>
<!-- <script src="tinawebJS/asyncFA2.js" type="text/javascript" language="javascript"></script> -->
<script src="tinawebJS/Tinaweb.js" type="text/javascript" language="javascript"></script>
......
......@@ -17,12 +17,6 @@ function changeGraphAppearanceByFacets( manualflag ) {
if ( !isUndef(manualflag) && !TW.conf.colorByAtt ) TW.conf.colorByAtt = manualflag;
if(!TW.conf.colorByAtt) return;
// for GUI html: if present, rename raw attribute key by a proper label
var AttsTranslations = {
}
// settings to function name
var colorFuns = {
'heatmap': "heatmapColoring",
......@@ -803,3 +797,96 @@ function activateModules() {
}
}
}
// Settings edition
// =================
function fillAttrsInForm() {
var actypes = getActivetypes()
for (let tid in actypes) {
let ty = actypes[tid]
let elChooser = document.getElementById('choose-attr')
// each facet family or clustering type was already prepared
for (let att in TW.Clusters[ty]) {
let opt = document.createElement('option')
opt.value = att
opt.innerText = att
elChooser.appendChild(opt)
console.log(opt)
}
}
}
function binmodeOpenNBins() {
let mainq = document.getElementById('attr-binmode')
let subq = document.getElementById('attr-nbins-div')
if (mainq.value == "samepop" || mainq.value == "samerange") {
subq.style.display = 'block'
}
else {
subq.style.display = 'none'
}
}
function showAttrConf() {
let attrTitle = this.value
let settings = TW.conf.facetOptions[attrTitle]
if (settings) {
document.getElementById('attr-col').value = settings.col
document.getElementById('attr-binmode').value = settings.binmode
document.getElementById('attr-translation').value = settings.menutransl
if(settings.n) {
document.getElementById('attr-nbins-div').style.display = 'block'
document.getElementById('attr-nbins').value = settings.n
}
}
}
// writes new attribute configuration from user form AND recreates facet bins
// processing time: ~~ 1.5 ms for 100 nodes
function newAttrConf() {
let attrTitle = document.getElementById('choose-attr').value
// read values from GUI
TW.conf.facetOptions[attrTitle] = {
'col': document.getElementById('attr-col').value,
'binmode': document.getElementById('attr-binmode').value,
'n': document.getElementById('attr-nbins').value,
'menutransl': document.getElementById('attr-translation').value
}
// find the corresponding types
let relevantTypes = {}
for (let ty in TW.Clusters) {
if (TW.Clusters[ty][attrTitle]) {
relevantTypes[ty] = true
}
}
// reparse values (avoids keeping them in RAM since parseCustom)
tmpVals = {}
for (let nid in TW.Nodes) {
let n = TW.Nodes[nid]
if (relevantTypes[n.type]) {
tmpVals = updateValueFacets(tmpVals, n, attrTitle)
}
}
let newClustering = facetsBinning (tmpVals)
// write result to global TW.Clusters
for (let ty in newClustering) {
TW.Clusters[ty][attrTitle] = newClustering[ty][attrTitle]
}
// update the GUI menu
changeGraphAppearanceByFacets(true)
// console.log("reparse raw result", tmpVals)
// console.log("reparse binned result", newClustering)
}
......@@ -332,7 +332,7 @@
/* small width or small height */
@media(max-width:768px), (max-height:500px) {
@media(max-width:768px), (max-height:550px) {
/* zoombar reduction */
.zoombarbuttons img {
......
/* ================= MAIN COMEX SERVICES STYLESHEET ================= */
@media all {
/* form content for reg, profile, login, etc.... */
div.uform {
/*padding: 15px 30px 53px 30px;*/
padding: 0px 5px 3px 5px;
margin: 7px auto 3px auto;
background-color: #ddd;
}
/* form content for reg, profile, login, etc.... */
div.uform-white {
padding: 0px 5px 3px 5px;
margin: 7px auto 3px auto;
background-color: #fff;
border-radius: 5px;
}
div.uform-white .ccsection-heading,
div.uform-white .ccsection-uform-body {
background-color: #fff;
}
/*div.uform::before {
content: "F O R M";
font-family: monospace;
font-size: 1em;
float: right;
color: #000;
}*/
button.clean-btn
/* not used at present but could be useful for autompleted inputs */
.autocomp {
}
/* a "subpage" container UNUSED */
.subpage {
width: 100%;
max-width: 65em;
display: none ; /*switching page <=> displaying it */
}
.cartouche {
border: .2em dotted #988 ;
border-radius: 2em;
padding: 1em 3em;
text-align: center;
/*max-width: 40em;*/
}
/* ex: profile overview in readonly */
.readonly.panel-body {
background-color: #ddd;
}
/* a fixed div for validation messages */
.menu-left-fixed {
position: fixed;
top: 4em;
left: 0;
display:block;
z-index: 4;
opacity: .8;
}
/* ==> a question + input block <== */
.question {
padding: 0 1em;
margin-bottom: 2em;
max-width: 57em !important; /* FORMWIDTH */
}
/* we center the questions
when there is a visible wrapping */
div.uform .question,
.ccsection-wrap .question {
margin-left: auto;
margin-right: auto;
}
.conditional-q {
display: none;
}
/* small label inside addon group*/
.smlabel {
min-width: 7.5em;
}
/* the question's additional legend or caption */
.legend {
font-family: Cambria, serif;
color: #554 ;
font-style: italic;
text-align:right;
padding: .5em 0 .5em .5em ;
margin: 0;
}
/* floats on the right of the question if set after it */
.legend-float {
float:right;
text-align:center;
font-size: 70%;
padding-right: 2.1em;
}
.legend-alone {
font-family: Cambria, serif;
color: #554 ;
font-style: italic;
text-align: center;
font-size: 120%;
padding-bottom: 1em;
font-weight: 500;
}
/* for code blocks or urls */
.code {
font-family: "Fira Mono", "Droid Sans Mono", monospace;
font-weight: 500;
font-size: 75% ;
background-color: #ACA;
padding: .2em;
border-radius: .2em;
}
/* form categories: like form sections etc. */
h3.formcat {
font-size: 16px;
padding-top: 1em;
font-weight: bold;
color: #910;
}
h3.formcatfirst {
font-size: 16px;
margin-top: .2em;
font-weight: bold;
color: #910;
}
.debug-info {
font-family: monospace ;
max-width: 55em;
font-size: 80%;
line-height: 90% ;
background-color: #555;
color: #fff;
/* to stay invisible when no debug message */
padding: 0 ;
min-height: 0px;
}
/* md and smaller */
@media(max-width:991px){
div.uform, div.uform-nobg {
padding: 0;
margin-top: 5px;
margin-bottom: 0;
}
.menu-left-fixed {
top: 11em;
}
.question {
padding: 0;
}
#main_message {
font-size: 110%;
line-height: 1em;
padding: .1em .3em ;
margin: 0;
}
}
......@@ -1010,6 +1010,11 @@ var TinaWebJS = function ( sigmacanvas ) {
}
}
// attributes' facet-options init & handler
fillAttrsInForm()
document.getElementById('choose-attr').onchange = showAttrConf
// cancelSelection(false);
}
......
......@@ -274,7 +274,7 @@ function sortNodeTypes(observedTypesDict) {
function facetsBinning (valuesIdx) {
console.debug("facetsBinning: valuesIdx", valuesIdx)
// console.debug("facetsBinning: valuesIdx", valuesIdx)
let facetIdx = {}
......@@ -921,7 +921,7 @@ function updateRelations(typedRelations, edgeCateg, srcId, tgtId){
// To fill the reverse map: values => nodeids of a given type
function updateValueFacets(facetIdx, aNode) {
function updateValueFacets(facetIdx, aNode, optionalFilter) {
if (!facetIdx[aNode.type]) facetIdx[aNode.type]={}
for (var at in aNode.attributes) {
......@@ -930,39 +930,43 @@ function updateValueFacets(facetIdx, aNode) {
if (at == 'category')
continue
let val = aNode.attributes[at]
// attribute filter undef or str: acceptedAttrName
if (isUndef(optionalFilter) || at == optionalFilter) {
let val = aNode.attributes[at]
if (!facetIdx[aNode.type][at]) facetIdx[aNode.type][at]={'vals':{'vstr':[], 'vnum':[]},'map':{}}
if (!facetIdx[aNode.type][at]) facetIdx[aNode.type][at]={'vals':{'vstr':[], 'vnum':[]},'map':{}}
// shortcut
var indx = facetIdx[aNode.type][at]
// shortcut
var indx = facetIdx[aNode.type][at]
// determine observed type of this single value
let castVal = Number(val)
// determine observed type of this single value
let castVal = Number(val)
// this discovered datatype will be a condition (no bins if not mostly numeric)
let dtype = ''
if (isNaN(castVal)) {
dtype = 'vstr'
}
else {
dtype = 'vnum'
val = castVal // we keep it as number
}
// this discovered datatype will be a condition (no bins if not mostly numeric)
let dtype = ''
if (isNaN(castVal)) {
dtype = 'vstr'
}
else {
dtype = 'vnum'
val = castVal // we keep it as number
}
if (!indx.map[val]) indx.map[val] = []
if (!indx.map[val]) indx.map[val] = []
indx.vals[dtype].push(val) // for ordered scale
indx.map[val].push(aNode.id) // inverted index
indx.vals[dtype].push(val) // for ordered scale
indx.map[val].push(aNode.id) // inverted index
// POSSIBLE with the discovered datatype
// => it would also allow to index text values (eg country, affiliation, etc.)
// with the strategy "most frequent distinct values" + "others"
// which would be useful (eg country, affiliation, etc.) !!!
// POSSIBLE with the discovered datatype
// => it would also allow to index text values (eg country, affiliation, etc.)
// with the strategy "most frequent distinct values" + "others"
// which would be useful (eg country, affiliation, etc.) !!!
}
}
return facetIdx
}
......
......@@ -915,8 +915,9 @@ function heatmapColoring(daclass) {
var tickThresholds = TW.Clusters[ty][daclass]
// verifications
if (tickThresholds.length != nColors) {
console.warn (`heatmapColoring setup mismatch: TW.Clusters ticks ${tickThresholds} from scanClusters should == nColors ${nColors}`)
if (tickThresholds.length - 1 != nColors) {
console.warn (`heatmapColoring setup mismatch: TW.Clusters ticks ${tickThresholds.length} - 1 non_numeric from scanClusters should == nColors ${nColors}`)
nColors = tickThresholds.length - 1
}
binColors = getHeatmapColors(nColors)
......@@ -964,7 +965,7 @@ function heatmapColoring(daclass) {
}
}
console.info('coloring distribution per tick thresholds' , totalsPerBinMin)
// console.info('coloring distribution per tick thresholds' , totalsPerBinMin)
// Edge precompute alt_rgb by new source-target nodes-colours combination
repaintEdges()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment