webui/js/wordcloud.js (122 lines of code) (raw):

/* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ const SVG_NAMESPACE = "http://www.w3.org/2000/svg" function fastIntersect(x,y,nx,ny) { if (x.getAttribute("id") == y.getAttribute("id")) { // can't collide with itself return false } let spacing = 2 // number of pixels to keep elements apart nx = nx ? nx : 0 ny = ny ? ny : 0 let a = x.getBoundingClientRect() let b = y.getBoundingClientRect() return !(b.left-spacing > (a.right+nx) || b.right+spacing < (a.left+nx) || b.top-spacing > (a.bottom+ny) || b.bottom+spacing < (a.top+ny)); } function makeWord(word, size, daterange) { let textBox = document.createElementNS(SVG_NAMESPACE, "text"); textBox.setAttribute("font-size", size + "px") textBox.setAttribute("x", "0") textBox.setAttribute("y", "40") textBox.setAttribute("class", "cloudword") textBox.setAttribute("onclick", "search(\"" + word + "\", '" + daterange + "')") textBox.textContent = word return textBox } async function wordCloud(hash, width, height, obj, daterange) { let total = 0 let boxes = [] let space = width * height for (let i in hash) { total += Math.sqrt(hash[i]) } let hashSorted = [] for (let word in hash) hashSorted.push(word) hashSorted = hashSorted.sort(function(a,b) { return hash[a] > hash[b] }) let svg = document.createElementNS(SVG_NAMESPACE, "svg"); document.body.appendChild(svg) svg.setAttribute("width", width) svg.setAttribute("height", height) svg.setAttribute("class", "wordcloud") for (let word of hashSorted) { let size = 0; let expected_area = ( Math.sqrt(hash[word]) / total ) * (space*0.9) let textBox = document.createElementNS(SVG_NAMESPACE, "text"); textBox.textContent = word textBox.setAttribute("font-size", "100px") svg.appendChild(textBox) let w = textBox.getBoundingClientRect(); for (let s = 100; s > 0; s-=5) { let area = w.width * w.height * ( (s/100)*(s/100) ); if (area <= expected_area ) { size = s; svg.removeChild(textBox) break } } let popped = false // Try with random placement textBox = makeWord(word, size, daterange) textBox.setAttribute("id", "svg_wc_" + word) svg.appendChild(textBox) if (!popped) { textBox.setAttribute("x", 0) textBox.setAttribute("y", 0) for (let ss = size; ss > 5; ss *= 0.9) { // alert(ss) if (popped) { break } textBox.setAttribute("font-size", ss + "px") w = textBox.getBoundingClientRect() for (let l = 0; l < 80; l++) { let nx = 4 + (Math.random() * (width-8-w.width)) let ny = 4 + w.height + ((l/80) * (height-8-w.height)) let it = false for (let box of boxes) { if (fastIntersect(textBox, box, nx, ny)) { it = true break } } if (it == false) { popped = true textBox.setAttribute("x", nx) textBox.setAttribute("y", ny) break } } } } if (popped) { let color = 'hsl('+ Math.random()*360 +', 40%, 50%)'; textBox.setAttribute("fill", color) boxes.push(textBox) } else { //alert("Could not add " + word) svg.removeChild(textBox) } } // Try to size up texts a bit for (let textBox of boxes) { let osize = parseFloat(textBox.getAttribute('font-size')) let psize = osize for (let n = 1; n < 1.4; n+=0.2) { let nsize = osize * n textBox.setAttribute("font-size", nsize + "px") let w = textBox.getBoundingClientRect() let good = true for (let box of boxes) { if (fastIntersect(textBox, box)) { good = false break } } if (!good || w.right > width-4 || w.top < 4) { textBox.setAttribute("font-size", psize + "px") break } psize = nsize } } document.body.removeChild(svg) obj.inject(svg); }