2

I am working on a world map with zoom and pan. I draw circles on certain cities and the radius of the circle is determined by the data. When mouse over these circle then a tooltip will show up displaying the data.

The structure of the code is // Zoom behavior is called on this selection - lets call this parentGroup
// To capture all mouse and touch events // Call this childGroup // Tooltip group hidden - will show/hide on mouseover // Call this toolTipGroup

The problem here is

  1. If the toolTipGgroup is attached to parentGroup and lives as a sibling to the childGroup then when the map is zoomed the tip position is messed up. I tried getting the (x,y) from the projection and applied a translate(x,y) on the tooltip. The tooltipGroup is translated but its irrelavant since zoom applied.
  2. If the toolTipGroup is attached to the childGroup, when the map is zoomed the toolTipGroup position is intact but its zoomed along with the childGroup. How to stop the tool tip from scaling along with the map (retain its original size)?

Note: I control the size of the circles by redrawing each of the circles on zoom behavior's callback with the new radius = oldRadius/currentScale

enter image description here enter image description here enter image description here

Akh
  • 5,961
  • 14
  • 53
  • 82
  • See either https://stackoverflow.com/questions/34323318/d3-us-state-map-with-markers-zooming-transform-issues or use semantic zoom as in http://bl.ocks.org/mbostock/2206340 – Lars Kotthoff Jan 29 '16 at 22:14
  • Thank you @Lars. The negation of scale works on an image but in my case it doesnt work on a toolTipGroup (includes text, rect image and path) – Akh Jan 29 '16 at 23:12
  • Then I would go with the semantic zoom. – Lars Kotthoff Jan 29 '16 at 23:13

2 Answers2

0

I don't understand your statement The negation of scale works on an image but in my case it doesnt work on a toolTipGroup (includes text, rect image and path). This should work just fine, wrap it all in a g and inverse scale that. The tricky part is recalculating the position of the tooltip with respect to the zoom. Here's a quick example which places a tooltip on my home city of Baltimore, MD:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.states {
  fill: none;
  stroke: #fff;
  stroke-linejoin: round;
}

</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>

var width = 960,
    height = 500;

var fill = d3.scale.log()
    .domain([10, 500])
    .range(["brown", "steelblue"]);

var path = d3.geo.path();

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);
    
var zoom = d3.behavior.zoom()
    .scaleExtent([1, 15])
    .on("zoom", zoomed);
    
svg.append("rect")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height);
    
var g = svg.append("g");

var tooltip,
    tipPosition = [722, 160],
    tipSize = [85,50];
    
d3.json("https://rawgit.com/mbostock/topojson/master/examples/us-10m.json", function(error, us) {
  if (error) throw error;

  g.append("g")
      .attr("class", "counties")
    .selectAll("path")
      .data(topojson.feature(us, us.objects.counties).features)
    .enter().append("path")
      .attr("d", path)
      .style("fill", function(d) { return fill(path.area(d)); });

  g.append("path")
      .datum(topojson.mesh(us, us.objects.states, function(a, b) { return a.id !== b.id; }))
      .attr("class", "states")
      .attr("d", path);
      
      tooltip = g.append("g")
  .attr("transform","translate(" + tipPosition + ")")
  .attr("class","tooltip");

tooltip
  .append("rect")
  .style("fill", "steelblue")
  .attr("width", tipSize[0])
  .attr("height", tipSize[1] - 10)
  .attr("rx", "10")
  
tooltip
  .append("path")
  .attr("d", function(){
    var rv = "";
    rv += "M";
    rv += tipSize[0]/2 - 5;
    rv += ","
    rv += tipSize[1] - 10;
    rv += "L";
    rv += tipSize[0]/2 + 5;
    rv += ","
    rv += tipSize[1] - 10;
    rv += "L";
    rv += tipSize[0]/2;
    rv += ","
    rv += tipSize[1];
    rv += "Z";
    return rv;
  })
  .style("fill", "steelblue");
  
tooltip
  .append("text")
  .text("Baltimore")
  .attr("transform","translate(" + (tipSize[0]/2) + "," + (tipSize[1]/2) + ")")
  .attr("text-anchor", "middle")
  .style("fill","black")
  .style("font-family","arial");
  
 svg
    .call(zoom)
    .call(zoom.event);
});


function zoomed() {
  // apply zoom
  g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
  // calculate position with respect to zoom
  var trans = [tipPosition[0]  + (tipSize[0]/2 * (1-1/d3.event.scale)), tipPosition[1] + (tipSize[1] * (1-1/d3.event.scale))];
  // inverse scale zoom
  tooltip.attr("transform", "translate(" + trans + ")scale(" + 1/d3.event.scale +")")
}

</script>
Mark
  • 106,305
  • 20
  • 172
  • 230
0

Moving the Tooltip group out of the path group solved the problem for me. But I had to calculate the get the location of the mouse pointer to calculate the tooltip location which was previously auto-calculated based on the circle center.

Akh
  • 5,961
  • 14
  • 53
  • 82