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

前端 未结 6 1618
有刺的猬
有刺的猬 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:10

    One option is to use the force layout with multiple foci. Each foci must be located in the feature's centroid, set up the label to be attracted only by the corresponding foci. This way, each label will tend to be near of the feature's centroid, but the repulsion with other labels may avoid the overlapping issue.

    For comparison:

    • M. Bostock's "Lets Make a Map" tutorial (resulting map),
    • my gist for an Automatic Labels Placement version (resulting map) implementing the foci's strategy.

    The relevant code:

    // Place and label location
    var foci = [],
        labels = [];
    
    // Store the projected coordinates of the places for the foci and the labels
    places.features.forEach(function(d, i) {
        var c = projection(d.geometry.coordinates);
        foci.push({x: c[0], y: c[1]});
        labels.push({x: c[0], y: c[1], label: d.properties.name})
    });
    
    // Create the force layout with a slightly weak charge
    var force = d3.layout.force()
        .nodes(labels)
        .charge(-20)
        .gravity(0)
        .size([width, height]);
    
    // Append the place labels, setting their initial positions to
    // the feature's centroid
    var placeLabels = svg.selectAll('.place-label')
        .data(labels)
        .enter()
        .append('text')
        .attr('class', 'place-label')
        .attr('x', function(d) { return d.x; })
        .attr('y', function(d) { return d.y; })
        .attr('text-anchor', 'middle')
        .text(function(d) { return d.label; });
    
    force.on("tick", function(e) {
        var k = .1 * e.alpha;
        labels.forEach(function(o, j) {
            // The change in the position is proportional to the distance
            // between the label and the corresponding place (foci)
            o.y += (foci[j].y - o.y) * k;
            o.x += (foci[j].x - o.x) * k;
        });
    
        // Update the position of the text element
        svg.selectAll("text.place-label")
            .attr("x", function(d) { return d.x; })
            .attr("y", function(d) { return d.y; });
    });
    
    force.start();
    

    enter image description here

提交回复
热议问题