Get tool tip for all overlapped features D3

Viewed 266

I'm try to figure out how we can add tooltip for feature that is behind some other features.Overlapped features in d3

For example in picture there are some circles overlapped to each other I want get information about both features/circles if mouseover on overlapped area.

Code and tooltip example is attached below.

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    radius = 32;

var circles = d3.range(20).map(function() {
  return {
    x: Math.round(Math.random() * (width - radius * 2) + radius),
    y: Math.round(Math.random() * (height - radius * 2) + radius)
  };
});

var color = d3.scaleOrdinal()
    .range(d3.schemeCategory20);

var tooltip = d3.select("body")
    .append("div")
    .style("position", "absolute")
    .style("z-index", "10")
    .style("visibility", "hidden")
    .text("a simple tooltip");

  
svg.selectAll("circle")
  .data(circles)
  .enter().append("circle")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })
    .attr("r", radius)
    .on("mouseover", function(d){
  return tooltip.style("visibility", "visible");
})
    .on("mousemove", function(d){
  return tooltip
    .html("Radius: " + d.x)
    .style("top", (event.pageY-10)+"px").style("left",      (event.pageX+10)+"px");
})
    .on("mouseout", function(d){return tooltip.style("visibility", "hidden");})
    
  .style("fill", function(d, i) { return color(i); })
    .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

function dragstarted(d) {
  d3.select(this).raise().classed("active", true);
}

function dragged(d) {
  d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}

function dragended(d) {
  d3.select(this).classed("active", false);
}
svg {
  border: 1px solid black;
}

.active {
  stroke: #000;
  stroke-width: 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>
<svg width="960" height="500"></svg>

1 Answers

This will calculate the mouse position's distance from all the other circles to test if the mouse is over more then one circle:

<!DOCTYPE html>

<html>
  <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>
    <svg width="500" height="500"></svg>

    <script>
      var svg = d3.select('svg'),
        width = +svg.attr('width'),
        height = +svg.attr('height'),
        radius = 32;

      var circles = d3.range(20).map(function () {
        return {
          x: Math.round(Math.random() * (width - radius * 2) + radius),
          y: Math.round(Math.random() * (height - radius * 2) + radius),
        };
      });

      var color = d3.scaleOrdinal().range(d3.schemeCategory20);

      var tooltip = d3
        .select('body')
        .append('div')
        .style('position', 'absolute')
        .style('z-index', '10')
        .style('visibility', 'hidden')
        .text('a simple tooltip');

      var circles = svg
        .selectAll('circle')
        .data(circles)
        .enter()
        .append('circle')
        .attr('cx', function (d) {
          return d.x;
        })
        .attr('cy', function (d) {
          return d.y;
        })
        .attr('r', radius)
        .on('mouseover', function (d) {
          return tooltip.style('visibility', 'visible');
        })
        .on('mousemove', function (d) {

          var txt = 'X: ' + d.x,
              m = d3.mouse(this);

          circles.each(function(d1){
            // if not circle with "mouseover"
            if (d.x !== d1.x || d.y !== d1.y)
            {
              // check distance
              if (Math.sqrt((d1.x - m[0])**2 + (d1.y - m[1])**2) <= 32)
              {
                // add to tooltip
                txt += '<br/> X: ' + d1.x;
              }
            }
          });
          return tooltip
            .html(txt)
            .style('top', event.pageY - 10 + 'px')
            .style('left', event.pageX + 10 + 'px');
        })
        .on('mouseout', function (d) {
          return tooltip.style('visibility', 'hidden');
        })

        .style('fill', function (d, i) {
          return color(i);
        })
        .call(
          d3
            .drag()
            .on('start', dragstarted)
            .on('drag', dragged)
            .on('end', dragended)
        );

      function dragstarted(d) {
        d3.select(this).raise().classed('active', true);
      }

      function dragged(d) {
        d3.select(this)
          .attr('cx', (d.x = d3.event.x))
          .attr('cy', (d.y = d3.event.y));
      }

      function dragended(d) {
        d3.select(this).classed('active', false);
      }
    </script>
  </body>
</html>

Related