|
@@ -0,0 +1,338 @@
|
|
|
+
|
|
|
+/**
|
|
|
+ * QUERY SECTION
|
|
|
+ *
|
|
|
+ * */
|
|
|
+
|
|
|
+// SPARQL Query text -- 'prefixes' and 'thisUrlParams' defined in people.js;
|
|
|
+let queryNetwork = prefixes + " SELECT COUNT(?event1) AS ?count1 COUNT(?event2) AS ?count2 ?uri2 SAMPLE(?label2) AS ?text \
|
|
|
+WHERE { \
|
|
|
+{?event1 rdf:type crm:EL1_Exchange_Letters . \
|
|
|
+?event_to rdfs:subClassOf ?event1; \
|
|
|
+rdf:type crm:EL2_Send_Letter ; \
|
|
|
+crm:P01_has_domain ?pc_to . \
|
|
|
+?pc_to crm:P02_has_range ?uri . \
|
|
|
+?uri rdfs:label ?label . \
|
|
|
+?event_from rdfs:subClassOf ?event1; \
|
|
|
+rdf:type crm:EL3_Receive_Letter; \
|
|
|
+crm:P01_has_domain ?pc_from . \
|
|
|
+?pc_from crm:P02_has_range ?uri2 . \
|
|
|
+?uri2 rdfs:label ?label2 . \
|
|
|
+FILTER (?uri = <" + thisUrlParams.link + ">) \
|
|
|
+} UNION { \
|
|
|
+?event2 rdf:type crm:EL1_Exchange_Letters . \
|
|
|
+?event_to rdfs:subClassOf ?event2; \
|
|
|
+rdf:type crm:EL3_Receive_Letter ; \
|
|
|
+crm:P01_has_domain ?pc_from . \
|
|
|
+?pc_from crm:P02_has_range ?uri . \
|
|
|
+?uri rdfs:label ?label . \
|
|
|
+?event_from rdfs:subClassOf ?event2; \
|
|
|
+rdf:type crm:EL2_Send_Letter; \
|
|
|
+crm:P01_has_domain ?pc_to . \
|
|
|
+?pc_to crm:P02_has_range ?uri2 . \
|
|
|
+?uri2 rdfs:label ?label2 . \
|
|
|
+FILTER (?uri = <" + thisUrlParams.link + ">) \
|
|
|
+} \
|
|
|
+}"
|
|
|
+
|
|
|
+// 'prepareQueryURL' defined in people.js
|
|
|
+let queryNet = prepareQueryURL(queryNetwork);
|
|
|
+
|
|
|
+// do the query, call data processor on successful output
|
|
|
+$.ajax({
|
|
|
+ url: queryNet,
|
|
|
+ dataType: "json",
|
|
|
+ success: (data) => handle_word_network(data)
|
|
|
+});
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * COMPONENT CREATION SECTION
|
|
|
+ *
|
|
|
+ * */
|
|
|
+
|
|
|
+// 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){
|
|
|
+ // Pre-process data
|
|
|
+ let tempArray = processQuery(json);
|
|
|
+ let words = prepareWords(tempArray);
|
|
|
+ // Create page elements
|
|
|
+ doListPersonNetwork(tempArray);
|
|
|
+ doWordCloud(words);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// Return structured object from raw query data for further processing
|
|
|
+function processQuery(json) {
|
|
|
+
|
|
|
+ const tempArray = [];
|
|
|
+
|
|
|
+ $.each(
|
|
|
+ json['results']['bindings'],
|
|
|
+ (index, value) => {
|
|
|
+ let text = value['text']['value'];
|
|
|
+ let link = value['uri2']['value'];
|
|
|
+ let sent = parseInt(value['count1']['value']);
|
|
|
+ let received = parseInt(value['count2']['value']);
|
|
|
+ let count = sent + received;
|
|
|
+ tempArray.push({'text': text, 'sent': sent, 'received': received, 'count': count, 'hlink': link});
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ tempArray.sort((a, b) => b.count - a.count);
|
|
|
+
|
|
|
+ return tempArray;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// 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
|
|
|
+function doListPersonNetwork(tempArray){
|
|
|
+
|
|
|
+ let ArrayNames = "";
|
|
|
+
|
|
|
+ tempArray.forEach(element => {
|
|
|
+ ArrayNames +=
|
|
|
+ "<div class='item-place-person'>\
|
|
|
+ <div class='item-place-person-label'>" +
|
|
|
+ element['text'] +
|
|
|
+ "<br /><span class='num_occ'>[Lettere inviate: " + element['sent'] + "]</span>\
|
|
|
+ <br /><span class='num_occ'>[Lettere ricevute: " + element['received'] + "]</span>\
|
|
|
+ <br /><span class='num_occ'>[Totale corrispondenza: " + element['count'] + "]</span>\
|
|
|
+ </div>\
|
|
|
+ <div class='item-place-person-action'>\
|
|
|
+ <div class='persona' id='" + element['hlink'] +"'>\
|
|
|
+ <i class='fa fa-user' style='cursor:pointer'></i>\
|
|
|
+ </div>\
|
|
|
+ </div>\
|
|
|
+ </div>";
|
|
|
+ });
|
|
|
+
|
|
|
+ document.getElementById("list_person_network").innerHTML = ArrayNames;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// Create the word cloud
|
|
|
+function doWordCloud(myWords){
|
|
|
+
|
|
|
+ // Clear word cloud div
|
|
|
+ $('#myWordCloud').empty();
|
|
|
+
|
|
|
+
|
|
|
+ // 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;
|
|
|
+
|
|
|
+ // append the svg object to the body of the page
|
|
|
+ let svg = d3.select("#myWordCloud")
|
|
|
+ .append("svg")
|
|
|
+ .attr("id", "wordcloudNetwork")
|
|
|
+ .attr("width", width + margin.left + margin.right)
|
|
|
+ .attr("height", height + margin.top + margin.bottom)
|
|
|
+ .attr("style", "border:1px solid black")
|
|
|
+ .append("g")
|
|
|
+ .attr("transform", "translate(" + (width*0.5) + "," + (height*0.5) + ")")
|
|
|
+ .attr("overflow","scroll");
|
|
|
+
|
|
|
+ // In case of empty cloud, draw special page and exit
|
|
|
+ if (myWords.length == 0){
|
|
|
+ drawEmptyWordCloud();
|
|
|
+ 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();
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// Helper function -- draw special empty word cloud if there are no occurrences
|
|
|
+function drawEmptyWordCloud(){
|
|
|
+
|
|
|
+ let wordIcon = "<div id='users_icon' class='no_info_icon'> \
|
|
|
+ <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 512'> \
|
|
|
+ <!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d='M319.9 320c57.41 0 103.1-46.56 103.1-104c0-57.44-46.54-104-103.1-104c-57.41 0-103.1 46.56-103.1 104C215.9 273.4 262.5 320 319.9 320zM369.9 352H270.1C191.6 352 128 411.7 128 485.3C128 500.1 140.7 512 156.4 512h327.2C499.3 512 512 500.1 512 485.3C512 411.7 448.4 352 369.9 352zM512 160c44.18 0 80-35.82 80-80S556.2 0 512 0c-44.18 0-80 35.82-80 80S467.8 160 512 160zM183.9 216c0-5.449 .9824-10.63 1.609-15.91C174.6 194.1 162.6 192 149.9 192H88.08C39.44 192 0 233.8 0 285.3C0 295.6 7.887 304 17.62 304h199.5C196.7 280.2 183.9 249.7 183.9 216zM128 160c44.18 0 80-35.82 80-80S172.2 0 128 0C83.82 0 48 35.82 48 80S83.82 160 128 160zM551.9 192h-61.84c-12.8 0-24.88 3.037-35.86 8.24C454.8 205.5 455.8 210.6 455.8 216c0 33.71-12.78 64.21-33.16 88h199.7C632.1 304 640 295.6 640 285.3C640 233.8 600.6 192 551.9 192z'/> \
|
|
|
+ </svg> \
|
|
|
+ <p>Nessuna persona trovata</p> \
|
|
|
+ </div>";
|
|
|
+
|
|
|
+ $("#wordcloudNetwork").css("display", "none");
|
|
|
+ $("#references_network").css("display", "none");
|
|
|
+
|
|
|
+ document.getElementById("myWordCloud").innerHTML = wordIcon;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// Customized word cloud code
|
|
|
+
|
|
|
+// Takes a list of words, returns the innerHTML for a Word Cloud as a string containing multiple <g> svg elements
|
|
|
+function createWordCloud(words){
|
|
|
+
|
|
|
+ const rectObjs = []
|
|
|
+ let allBooms = 0
|
|
|
+ let attempts = 0;
|
|
|
+ for(let ind=0;ind<words.length;ind++){
|
|
|
+
|
|
|
+ let id0 = "word"+ind;
|
|
|
+ let praaaa = document.getElementById(id0).getBoundingClientRect();
|
|
|
+ //console.log('BB of '+id0, praaaa);
|
|
|
+
|
|
|
+ attempts++;
|
|
|
+ let boom = false;
|
|
|
+ // rectangle definition
|
|
|
+// let width = 120 + Math.round(50*Math.random());
|
|
|
+// let height = 40;
|
|
|
+ let fontSize = getFontSize(ind);
|
|
|
+ let width = praaaa.width;
|
|
|
+ //let height = fontSize;
|
|
|
+ let height = praaaa.height;
|
|
|
+ let xR = 1250 + Math.round( (Math.random()-0.5)*(1000 + 6*ind) );
|
|
|
+ let yR = 750 + Math.round( (Math.random()-0.5)*(400 + 4*ind) );
|
|
|
+ let angle = 90*(Math.floor(3*Math.random())-1);
|
|
|
+ let text = words[ind];
|
|
|
+ //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()));
|
|
|
+ let rects = rectTexts.join('\n');
|
|
|
+
|
|
|
+ return rects;
|
|
|
+}
|
|
|
+
|
|
|
+function getFontSize(weight){
|
|
|
+ return Math.max(80-weight, 30);
|
|
|
+}
|