How to avoid overlapping text elements on TreeMap when children open in D3.js?

I created the Tree in D3.js based on the Mike Bostock Node tree. The problem that I have, and what I also see in the Mike Tree, is that the text label overlaps / overlaps the nodes of the circle when there is not enough space, and does not expand the links to leave some space.

As a new user, I am not allowed to upload images, so here is the link

+6
source share
1 answer

Thus, the following approach can give different layout levels of different heights. You should make sure that with a radial arrangement you risk not having enough distribution for small circles to inflate the text without overlapping, but this time ignore it.

The key is to understand that the layout of the tree simply matches things with an arbitrary space of width and height and that the diagonal projector displays the width (x) in the corner and the height (y) in the radius. In addition, the radius is a simple function of the depth of the tree.

So here is a way to reassign depths based on text length:

First of all, I use the following (jQuery) to calculate the maximum text sizes for:

var computeMaxTextSize = function(data, fontSize, fontName){ var maxH = 0, maxW = 0; var div = document.createElement('div'); document.body.appendChild(div); $(div).css({ position: 'absolute', left: -1000, top: -1000, display: 'none', margin:0, padding:0 }); $(div).css("font", fontSize + 'px '+fontName); data.forEach(function(d) { $(div).html(d); maxH = Math.max(maxH, $(div).outerHeight()); maxW = Math.max(maxW, $(div).outerWidth()); }); $(div).remove(); return {maxH: maxH, maxW: maxW}; } 

Now I will recursively build an array with an array of strings per level:

 var allStrings = [[]]; var childStrings = function(level, n) { var a = allStrings[level]; a.push(n.name); if(n.children && n.children.length > 0) { if(!allStrings[level+1]) { allStrings[level+1] = []; } n.children.forEach(function(d) { childStrings(level + 1, d); }); } }; childStrings(0, root); 

And then calculate the maximum text length per level.

 var maxLevelSizes = []; allStrings.forEach(function(d, i) { maxLevelSizes.push(computeMaxTextSize(allStrings[i], '10', 'sans-serif')); }); 

Then I calculate the full width of the text for all levels (adding an interval for the icons of the small circle and some padding to make it look beautiful). This will be the radius of the final layout. Please note that I will use the same amount of supplements again later.

 var padding = 25; // Width of the blue circle plus some spacing var totalRadius = d3.sum(maxLevelSizes, function(d) { return d.maxW + padding}); var diameter = totalRadius * 2; // was 960; var tree = d3.layout.tree() .size([360, totalRadius]) .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; }); 

Now we can name the layout, as usual. There is one last fragment: to determine the radius for different levels, we need the cumulative sum of the radii of the previous levels. After that, we simply assign new radii to the computed nodes.

 // Compute cummulative sums - these will be the ring radii var newDepths = maxLevelSizes.reduce(function(prev, curr, index) { prev.push(prev[index] + curr.maxW + padding); return prev; },[0]); var nodes = tree.nodes(root); // Assign new radius based on depth nodes.forEach(function(d) { dy = newDepths[d.depth]; }); 

Oh voila! This may not be the cleanest solution, and it may not be all issues, but you need to get started. Enjoy!

+7
source

Source: https://habr.com/ru/post/924832/


All Articles