D3js: Automatic labels placement to avoid overlaps? (force repulsion)

前端 未结 6 1625
有刺的猬
有刺的猬 2020-11-27 11:28

How to apply force repulsion on map\'s labels so they find their right places automatically ?


Bostock\' \"Let\'s Make a Map\"

Mike

6条回答
  •  悲哀的现实
    2020-11-27 12:12

    While ShareMap-dymo.js may work, it does not appear to be very well documented. I have found a library that works for the more general case, is well documented and also uses simulated annealing: D3-Labeler

    I've put together a usage sample with this jsfiddle.The D3-Labeler sample page uses 1,000 iterations. I have found this is rather unnecessary and that 50 iterations seems to work quite well - this is very fast even for a few hundred data points. I believe there is room for improvement both in the way this library integrates with D3 and in terms of efficiency, but I wouldn't have been able to get this far on my own. I'll update this thread should I find the time to submit a PR.

    Here is the relevant code (see the D3-Labeler link for further documentation):

    var label_array = [];
    var anchor_array = [];
    
    //Create circles
    svg.selectAll("circle")
    .data(dataset)
    .enter()
    .append("circle")
    .attr("id", function(d){
        var text = getRandomStr();
        var id = "point-" + text;
        var point = { x: xScale(d[0]), y: yScale(d[1]) }
        var onFocus = function(){
            d3.select("#" + id)
                .attr("stroke", "blue")
                .attr("stroke-width", "2");
        };
        var onFocusLost = function(){
            d3.select("#" + id)
                .attr("stroke", "none")
                .attr("stroke-width", "0");
        };
        label_array.push({x: point.x, y: point.y, name: text, width: 0.0, height: 0.0, onFocus: onFocus, onFocusLost: onFocusLost});
        anchor_array.push({x: point.x, y: point.y, r: rScale(d[1])});
        return id;                                   
    })
    .attr("fill", "green")
    .attr("cx", function(d) {
        return xScale(d[0]);
    })
    .attr("cy", function(d) {
        return yScale(d[1]);
    })
    .attr("r", function(d) {
        return rScale(d[1]);
    });
    
    //Create labels
    var labels = svg.selectAll("text")
    .data(label_array)
    .enter()
    .append("text")
    .attr("class", "label")
    .text(function(d) {
        return d.name;
    })
    .attr("x", function(d) {
        return d.x;
    })
    .attr("y", function(d) {
        return d.y;
    })
    .attr("font-family", "sans-serif")
    .attr("font-size", "11px")
    .attr("fill", "black")
    .on("mouseover", function(d){
        d3.select(this).attr("fill","blue");
        d.onFocus();
    })
    .on("mouseout", function(d){
        d3.select(this).attr("fill","black");
        d.onFocusLost();
    });
    
    var links = svg.selectAll(".link")
    .data(label_array)
    .enter()
    .append("line")
    .attr("class", "link")
    .attr("x1", function(d) { return (d.x); })
    .attr("y1", function(d) { return (d.y); })
    .attr("x2", function(d) { return (d.x); })
    .attr("y2", function(d) { return (d.y); })
    .attr("stroke-width", 0.6)
    .attr("stroke", "gray");
    
    var index = 0;
    labels.each(function() {
        label_array[index].width = this.getBBox().width;
        label_array[index].height = this.getBBox().height;
        index += 1;
    });
    
    d3.labeler()
        .label(label_array)
        .anchor(anchor_array)
        .width(w)
        .height(h)
        .start(50);
    
    labels
        .transition()
        .duration(800)
        .attr("x", function(d) { return (d.x); })
        .attr("y", function(d) { return (d.y); });
    
    links
        .transition()
        .duration(800)
        .attr("x2",function(d) { return (d.x); })
        .attr("y2",function(d) { return (d.y); });
    

    For a more in depth look at how D3-Labeler works, see "A D3 plug-in for automatic label placement using simulated annealing"

    Jeff Heaton's "Artificial Intelligence for Humans, Volume 1" also does an excellent job at explaining the simulated annealing process.

提交回复
热议问题