|
@@ -55,10 +55,9 @@ $.ajax({
|
|
// Master function, takes data output from the query and creates the HTML for the Word Cloud component of the page
|
|
// Master function, takes data output from the query and creates the HTML for the Word Cloud component of the page
|
|
function handle_word_network(json){
|
|
function handle_word_network(json){
|
|
// Pre-process data
|
|
// Pre-process data
|
|
- let tempArray = processQuery(json);
|
|
|
|
- let words = prepareWords(tempArray);
|
|
|
|
|
|
+ let words = processQuery(json);
|
|
// Create page elements
|
|
// Create page elements
|
|
- doListPersonNetwork(tempArray);
|
|
|
|
|
|
+ doListPersonNetwork(words);
|
|
doWordCloud(words);
|
|
doWordCloud(words);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -87,56 +86,6 @@ function processQuery(json) {
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-// Define font size, according to the number of words and limiting the impact of huge counts -- to refactor??
|
|
|
|
-function prepareWords(tempArray){
|
|
|
|
-
|
|
|
|
- const words = [];
|
|
|
|
-
|
|
|
|
- if (tempArray.length < 8) { // Less than 8 results -- font size is just = count/2 + constant
|
|
|
|
- for (let k=tempArray.length-1; k>=0; k--) {
|
|
|
|
- let text = tempArray[k]['text'];
|
|
|
|
- let preSize = tempArray[k]['count'] + 36;
|
|
|
|
- console.log('mann', preSize)
|
|
|
|
- let link = tempArray[k]['hlink'];
|
|
|
|
- words.push(
|
|
|
|
- {
|
|
|
|
- 'text': text,
|
|
|
|
- 'size': preSize/2 + 6,
|
|
|
|
- 'hlink': link
|
|
|
|
- }
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- } else { // More than 8 results -- font size = count except for especially "big" words
|
|
|
|
- let temp = 0;
|
|
|
|
- for (let k=tempArray.length-1; k>=0; k--) {
|
|
|
|
- let text = tempArray[k]['text'];
|
|
|
|
- let count = tempArray[k]['count'];
|
|
|
|
- let link = tempArray[k]['hlink'];
|
|
|
|
- let preSize = 0;
|
|
|
|
- if ((count - temp) > 50) {
|
|
|
|
- preSize = temp + 12;
|
|
|
|
- } else {
|
|
|
|
- preSize = count;
|
|
|
|
- }
|
|
|
|
- words.push(
|
|
|
|
- {
|
|
|
|
- 'text': text,
|
|
|
|
- 'size': preSize/2 + 6,
|
|
|
|
- 'hlink': link
|
|
|
|
- }
|
|
|
|
- );
|
|
|
|
- temp = preSize;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- console.log('tempArray', tempArray)
|
|
|
|
- console.log('words', words)
|
|
|
|
-
|
|
|
|
- return words;
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
// Create formatted list of people in the network
|
|
// Create formatted list of people in the network
|
|
function doListPersonNetwork(tempArray){
|
|
function doListPersonNetwork(tempArray){
|
|
|
|
|
|
@@ -165,7 +114,7 @@ function doListPersonNetwork(tempArray){
|
|
|
|
|
|
|
|
|
|
// Create the word cloud
|
|
// Create the word cloud
|
|
-function doWordCloud(myWords){
|
|
|
|
|
|
+function doWordCloud(words){
|
|
|
|
|
|
// Clear word cloud div
|
|
// Clear word cloud div
|
|
$('#myWordCloud').empty();
|
|
$('#myWordCloud').empty();
|
|
@@ -173,94 +122,45 @@ function doWordCloud(myWords){
|
|
|
|
|
|
// OVERALL GRAPHIC SETTINGS for the cloud container -- id, dimensions, position
|
|
// OVERALL GRAPHIC SETTINGS for the cloud container -- id, dimensions, position
|
|
|
|
|
|
- // set the dimensions and margins of the graph
|
|
|
|
- let modifierWidth = 0.4;
|
|
|
|
- let modifierHeigth = 0.4;
|
|
|
|
- let marginHeight = 0;
|
|
|
|
- let marginWidth = 0;;
|
|
|
|
-
|
|
|
|
- let margin = {
|
|
|
|
- top: marginHeight,
|
|
|
|
- right: marginWidth,
|
|
|
|
- bottom: marginHeight,
|
|
|
|
- left: marginWidth};
|
|
|
|
- let width = modifierWidth*window.innerWidth;
|
|
|
|
- let height = modifierHeigth*window.innerHeight;
|
|
|
|
|
|
+ let width = 0.5*window.innerWidth;
|
|
|
|
+ let height = 0.6*width;
|
|
|
|
|
|
// append the svg object to the body of the page
|
|
// append the svg object to the body of the page
|
|
let svg = d3.select("#myWordCloud")
|
|
let svg = d3.select("#myWordCloud")
|
|
.append("svg")
|
|
.append("svg")
|
|
.attr("id", "wordcloudNetwork")
|
|
.attr("id", "wordcloudNetwork")
|
|
- .attr("width", width + margin.left + margin.right)
|
|
|
|
- .attr("height", height + margin.top + margin.bottom)
|
|
|
|
|
|
+ .attr("preserveAspectRatio", "xMinYMin meet")
|
|
|
|
+ .attr("viewBox", "0 0 " + width + " " + height+'"')
|
|
|
|
+ .classed("svg-content", true)
|
|
|
|
+ .attr("width", width)
|
|
|
|
+ .attr("height", height)
|
|
.attr("style", "border:1px solid black")
|
|
.attr("style", "border:1px solid black")
|
|
- .append("g")
|
|
|
|
- .attr("transform", "translate(" + (width*0.75) + "," + (height*0.78) + ")")
|
|
|
|
- .attr("overflow","scroll");
|
|
|
|
|
|
|
|
// In case of empty cloud, draw special page and exit
|
|
// In case of empty cloud, draw special page and exit
|
|
- if (myWords.length == 0){
|
|
|
|
|
|
+ if (words.length == 0){
|
|
drawEmptyWordCloud();
|
|
drawEmptyWordCloud();
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
- // THE ACTUAL CLOUD
|
|
|
|
|
|
|
|
- // Constructs a new cloud layout instance. It runs an algorithm to find the position of words that suits your requirements
|
|
|
|
- // Wordcloud features that are different from one word to the other must be here
|
|
|
|
-
|
|
|
|
- let layout = d3.layout.cloud() // Constructs the cloud
|
|
|
|
- .size([1.5*width, 1.2*height]) // Sets width and height
|
|
|
|
- .words(myWords) // put words in; expects an array of objects. Format is free, it all depends on the definition of the functions that are applied to define the cloud structure.
|
|
|
|
- //
|
|
|
|
- // A js curiosity: ~ is a bitwise not operator. On a number, the double bitwise not ~~ has the net effect of giving the integer part, that is: floor on positives and ceiling on negatives.
|
|
|
|
- .rotate( () => { return ~~(Math.random() * 2);} )
|
|
|
|
- .fontSize( word => { return word['size']; } ) // font size of words
|
|
|
|
- .on("end", word => draw(word, svg));
|
|
|
|
- layout.start();
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-// Helper function for the Word Cloud --
|
|
|
|
-// it draws the words
|
|
|
|
-// Wordcloud features that are THE SAME from one word to the other can be here ??
|
|
|
|
-function draw(words, svg) {
|
|
|
|
- let cloud = svg.selectAll("g text")
|
|
|
|
- .data(words, word => word.text) // What does this do?
|
|
|
|
-
|
|
|
|
- //Entering words
|
|
|
|
- cloud.enter()
|
|
|
|
- .append("text")
|
|
|
|
- .style("font-family", "Impact")
|
|
|
|
- .attr("text-anchor", "middle")
|
|
|
|
- .attr('font-size', 1)
|
|
|
|
- .append("a")
|
|
|
|
- .attr("href", word => word.hlink)
|
|
|
|
- .attr("data-toggle", "tooltip")
|
|
|
|
- .text( word => { return word.text; });
|
|
|
|
-
|
|
|
|
- //Entering and exiting words
|
|
|
|
- cloud.transition()
|
|
|
|
- .duration(600)
|
|
|
|
- .style("font-size", function(d) { return d.size + "px"; })
|
|
|
|
- .attr("transform", function(d) {
|
|
|
|
- return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
|
|
|
|
- })
|
|
|
|
- .style("fill-opacity", 1);
|
|
|
|
-
|
|
|
|
- //Exiting words
|
|
|
|
- cloud.exit()
|
|
|
|
- .transition()
|
|
|
|
- .duration(200)
|
|
|
|
- .style('fill-opacity', 1e-6)
|
|
|
|
- .attr('font-size', 1)
|
|
|
|
- .remove();
|
|
|
|
|
|
+ // THE ACTUAL CLOUD
|
|
|
|
+ svg = document.getElementById('wordcloudNetwork');
|
|
|
|
+ console.log(svg);
|
|
|
|
+ console.log('CAZZO', svg.getBoundingClientRect());
|
|
|
|
+ wcParameters = {
|
|
|
|
+ svgWidth: svg.getBoundingClientRect().width,
|
|
|
|
+ svgHeight: svg.getBoundingClientRect().height,
|
|
|
|
+ maxFontSize: 20,
|
|
|
|
+ minFontSize: 5
|
|
|
|
+ }
|
|
|
|
+ let svgWords = hackWords(words, wcParameters);
|
|
|
|
+ svg.innerHTML = svgWords;
|
|
|
|
|
|
|
|
+ let wordCloudText = createWordCloud(words, wcParameters);
|
|
|
|
+ svg.innerHTML = wordCloudText;
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
// Helper function -- draw special empty word cloud if there are no occurrences
|
|
// Helper function -- draw special empty word cloud if there are no occurrences
|
|
function drawEmptyWordCloud(){
|
|
function drawEmptyWordCloud(){
|
|
|
|
|
|
@@ -277,3 +177,97 @@ function drawEmptyWordCloud(){
|
|
document.getElementById("myWordCloud").innerHTML = wordIcon;
|
|
document.getElementById("myWordCloud").innerHTML = wordIcon;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*****************************
|
|
|
|
+* Customized word cloud code *
|
|
|
|
+*****************************/
|
|
|
|
+
|
|
|
|
+function hackWords(words, parameters){
|
|
|
|
+
|
|
|
|
+ let weights = words.map(word => word.count);
|
|
|
|
+ let minWeight = Math.min(...weights);
|
|
|
|
+ let maxFontSize = parameters.maxFontSize;
|
|
|
|
+ let minFontSize = parameters.minFontSize;
|
|
|
|
+ console.log('weights', weights);
|
|
|
|
+ console.log(maxFontSize);
|
|
|
|
+ console.log(minFontSize);
|
|
|
|
+ let stringOfWords = "";
|
|
|
|
+
|
|
|
|
+ for(let ind=0; ind<words.length; ind++){
|
|
|
|
+
|
|
|
|
+ let word = words[ind];
|
|
|
|
+ word.fontSize = getFontSize(words[ind].count, minWeight, maxFontSize, minFontSize);
|
|
|
|
+ stringOfWords = stringOfWords+
|
|
|
|
+ '<text x="100" y ="' + (maxFontSize + maxFontSize*ind).toString() + '" + font-family="Verdana" font-size="'+(word.fontSize)+'" id="word' + ind + '">'+words[ind].text+'</text>\n';
|
|
|
|
+ }
|
|
|
|
+ return stringOfWords;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// Takes a list of words, returns the innerHTML for a Word Cloud as a string containing multiple <g> svg elements
|
|
|
|
+function createWordCloud(words, parameters){
|
|
|
|
+
|
|
|
|
+ let actualUsefulWidth = parameters.svgWidth;
|
|
|
|
+ let actualUsefulHeight = parameters.svgHeight;
|
|
|
|
+
|
|
|
|
+ const rectObjs = []
|
|
|
|
+ let allBooms = 0
|
|
|
|
+ let attempts = 0;
|
|
|
|
+
|
|
|
|
+ for(let ind=0;ind<words.length;ind++){
|
|
|
|
+ //for(let ind=0;ind<3;ind++){
|
|
|
|
+
|
|
|
|
+ let word = words[ind];
|
|
|
|
+ let id0 = "word"+ind;
|
|
|
|
+ console.log('id0', id0);
|
|
|
|
+ let wordBB = document.getElementById(id0).getBoundingClientRect();
|
|
|
|
+
|
|
|
|
+ attempts++;
|
|
|
|
+ let boom = false;
|
|
|
|
+ // rectangle definition
|
|
|
|
+ let fontSize = word.fontSize;
|
|
|
|
+ let width = wordBB.width;
|
|
|
|
+ let height = wordBB.height;
|
|
|
|
+ let xR = actualUsefulWidth + (Math.random()-0.5)*actualUsefulWidth - width;
|
|
|
|
+ let yR = actualUsefulHeight/2 + (Math.random()-0.5)*actualUsefulHeight - width;
|
|
|
|
+ //let xR = 0;
|
|
|
|
+ //let yR = 0;
|
|
|
|
+ //if(ind>0) yR = actualUsefulHeight-fontSize;
|
|
|
|
+ //if(ind>1) xR = actualUsefulWidth-width;
|
|
|
|
+ let angle;
|
|
|
|
+ if(word.received/word.sent>2) angle = 90;
|
|
|
|
+ else if(word.received/word.sent<1/2) angle = 270;
|
|
|
|
+ angle = 0;
|
|
|
|
+ let text = word.text;
|
|
|
|
+ //console.log('Rect:', width, height, xR, yR, angle, text);
|
|
|
|
+ let newRect = new Rect(width, height, xR, yR, angle, text, fontSize);
|
|
|
|
+ //console.log('nnn', newRect);
|
|
|
|
+ for(let rect of rectObjs){
|
|
|
|
+ boom = checkColl(newRect, rect);
|
|
|
|
+ if(boom) break;
|
|
|
|
+ }
|
|
|
|
+ if(boom){
|
|
|
|
+ allBooms++;
|
|
|
|
+ if(allBooms<10) ind--;
|
|
|
|
+ else(allBooms = 0);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ allBooms = 0;
|
|
|
|
+ rectObjs.push(newRect);
|
|
|
|
+ }
|
|
|
|
+ console.log('Attempted:', attempts);
|
|
|
|
+ console.log('Placed:', rectObjs.length);
|
|
|
|
+
|
|
|
|
+ const rectTexts = [];
|
|
|
|
+ rectObjs.forEach(rect => rectTexts.push(rect.printCoords(false)));
|
|
|
|
+ let rects = rectTexts.join('\n');
|
|
|
|
+
|
|
|
|
+ return rects;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function getFontSize(weight, minWeight, maxFontSize, minFontSize){
|
|
|
|
+ let r1 = weight/10*minWeight - 1/10; // between 0 and a big num
|
|
|
|
+ let res = minFontSize + (maxFontSize-minFontSize)*r1/Math.sqrt(1+r1**2);
|
|
|
|
+ return res;
|
|
|
|
+}
|