Kartikeya Sharma
Kartikeya Sharma

Reputation: 1383

Trying to create line chart with Circle Tooltip

I am trying to create an interactive line chart with circle tooltip like the following https://bl.ocks.org/alandunning/cfb7dcd7951826b9eacd54f0647f48d3 .

My code is below: HTML

<head>
  <style>
    circle {
      fill: steelblue;
    }

    body {
      font: 12px Arial;
    }

    path {
      stroke: steelblue;
      stroke-width: 2;
      fill: none;
    }

    .axis path,
    .axis line {
      fill: none;
      stroke: grey;
      stroke-width: 1;
      shape-rendering: crispEdges;
    }

    div.tooltip {
      position: absolute;
      text-align: center;
      width: 80px;
      height: 64px;
      padding: 2px;
      font: 14px sans-serif;
      color: black;
      background: lightsteelblue;
      border: 0px;
      border-radius: 8px;
      pointer-events: none;
    }

    .overlay {
      fill: none;
      pointer-events: all;
    }

    .focus circle {
      fill: #F1F3F3;
      stroke: #6F257F;
      stroke-width: 5px;
    }

    .hover-line {
      stroke: #6F257F;
      stroke-width: 2px;
      stroke-dasharray: 3, 3;
    }
  </style>

</head>

<body>
  <svg class='line-chart2'></svg>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="./regression.js"></script>

</body>

regression.js

var gdp = [387.65, 410.32, 415.73, 452.69, 462.14,
  478.96, 508.06, 599.59, 699.68, 808.90,
  920.31, 1201.11, 1186.95, 1323.94, 1656.61,
  1823.04, 1827.63, 1856.72, 2039.12, 2102.39,
  2274.22, 2600.81
]; //y or GDP of India
var years = ['1996', '1997', '1998', '1999', '2000', '2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017'];

var data_gdp = []
for (i = 0; i < years.length; i++) {
  data_gdp.push({
    year: years[i],
    value: gdp[i]
  })
}

function drawChart_gdp(data, class_name) {
  var svgWidth = 1200,
    svgHeight = 400;
  var margin = {
    top: 60,
    right: 60,
    bottom: 60,
    left: 60
  };
  var width = svgWidth - margin.left - margin.right;
  var height = svgHeight - margin.top - margin.bottom;
  var svg = d3.select(class_name)
    .attr("width", svgWidth)
    .attr("height", svgHeight);
  var bisectDate = d3.bisector(function(d) {
    return d.year;
  }).left;
  var g = svg.append("g")
    .attr("transform",
      "translate(" + margin.left + "," + margin.top + ")"
    );
  var x = d3.scaleTime().range([0, width]);
  var y = d3.scaleLinear().rangeRound([height, 0]);

  var line = d3.line()
    .x(function(d) {
      return x(new Date(parseInt(d.year), 0))
    })
    .y(function(d) {
      return y(d.value)
    })
  x.domain(d3.extent(data, function(d) {
    return new Date(parseInt(d.year), 0);
  }));
  y.domain(d3.extent(data, function(d) {
    return d.value
  }));

  g.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x))
    .append("text")
    .attr("fill", "#000")
    .text("Year")
    .attr("dy", "1.90em")
    .attr("y", 5)
    .attr("x", 500)
    .attr("font-size", "20px")
    .select(".domain")
    .remove();

  g.append("g")
    .call(d3.axisLeft(y))
    .append("text")
    .attr("fill", "#000")
    .attr("transform", "rotate(-90)")
    .attr("y", -80)
    .attr("x", -55)
    .attr("dy", "1.90em")
    .attr("text-anchor", "center")
    .attr("font-size", "20px")
    .text("GDP ($)")

  g.append("path")
    .datum(data)
    .attr("fill", "none")
    .attr("stroke", "steelblue")
    .attr("stroke-linejoin", "round")
    .attr("stroke-linecap", "round")
    .attr("stroke-width", 1.5)
    .attr("d", line);

  var focus = g.append("g")
    .attr("class", "focus")
    .style("display", "none");

  focus.append("line")
    .attr("class", "x-hover-line hover-line")
    .attr("y1", 0)
    .attr("y2", height);

  focus.append("line")
    .attr("class", "y-hover-line hover-line")
    .attr("x1", width)
    .attr("x2", width);

  focus.append("circle")
    .attr("r", 7.5);

  focus.append("text")
    .attr("x", 15)
    .attr("dy", ".31em");

  svg.append("rect")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height)
    .on("mouseover", function() {
      focus.style("display", null);
    })
    .on("mouseout", function() {
      focus.style("display", "none");
    })
    .on("mousemove", function() { //problem in this function
      var x0 = d3.timeFormat("%Y")(x.invert(d3.mouse(this)[0])),
        i = bisectDate(data, x0, 1);
      d0 = data[i - 1],
        d1 = data[i],
        d = x0 - d0.year > d1.year - x0 ? d1 : d0;
      focus.attr("transform", "translate(" + x(d.year) + "," + y(d.value) + ")");
      focus.select("text").text(function() {
        return d.value;
      });
      focus.select(".x-hover-line").attr("y2", height - y(d.value));
      focus.select(".y-hover-line").attr("x2", width + width);
    });

}
drawChart_gdp(data_gdp, '.line-chart2');

I am suspecting that in the example the data is being pulled from json file but here is pulled from an array where the problem comes for and also I think the kind of data is little bit different too. My goals is just to create a circle tool which shows the value of y-axis.

Upvotes: 1

Views: 215

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102194

The problem you're facing now is almost the opposite of the one you faced in your previous question: in that question, the problem was that you were dealing with date objects as if they were strings.

Now, you're dealing with strings as if they were date objects. In your data, the year is just a string, like "1996".

So, this:

focus.attr("transform", "translate(" + x(d.year) + "," + y(d.value) + ")");

Should be:

focus.attr("transform", "translate(" + x(d3.timeParse("%Y")(d.year)) + "," + y(d.value) + ")"); 
//parsing to a date here----------------------^

Here is the code with that change:

var gdp = [387.65, 410.32, 415.73, 452.69, 462.14,
  478.96, 508.06, 599.59, 699.68, 808.90,
  920.31, 1201.11, 1186.95, 1323.94, 1656.61,
  1823.04, 1827.63, 1856.72, 2039.12, 2102.39,
  2274.22, 2600.81
]; //y or GDP of India
var years = ['1996', '1997', '1998', '1999', '2000', '2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017'];

var data_gdp = []
for (i = 0; i < years.length; i++) {
  data_gdp.push({
    year: years[i],
    value: gdp[i]
  })
}

function drawChart_gdp(data, class_name) {
  var svgWidth = 1200,
    svgHeight = 400;
  var margin = {
    top: 60,
    right: 60,
    bottom: 60,
    left: 60
  };
  var width = svgWidth - margin.left - margin.right;
  var height = svgHeight - margin.top - margin.bottom;
  var svg = d3.select(class_name)
    .attr("width", svgWidth)
    .attr("height", svgHeight);
  var bisectDate = d3.bisector(function(d) {
    return d.year;
  }).left;
  var g = svg.append("g")
    .attr("transform",
      "translate(" + margin.left + "," + margin.top + ")"
    );
  var x = d3.scaleTime().range([0, width]);
  var y = d3.scaleLinear().rangeRound([height, 0]);

  var line = d3.line()
    .x(function(d) {
      return x(new Date(parseInt(d.year), 0))
    })
    .y(function(d) {
      return y(d.value)
    })
  x.domain(d3.extent(data, function(d) {
    return new Date(parseInt(d.year), 0);
  }));
  y.domain(d3.extent(data, function(d) {
    return d.value
  }));

  g.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x))
    .append("text")
    .attr("fill", "#000")
    .text("Year")
    .attr("dy", "1.90em")
    .attr("y", 5)
    .attr("x", 500)
    .attr("font-size", "20px")
    .select(".domain")
    .remove();

  g.append("g")
    .call(d3.axisLeft(y))
    .append("text")
    .attr("fill", "#000")
    .attr("transform", "rotate(-90)")
    .attr("y", -80)
    .attr("x", -55)
    .attr("dy", "1.90em")
    .attr("text-anchor", "center")
    .attr("font-size", "20px")
    .text("GDP ($)")

  g.append("path")
    .datum(data)
    .attr("fill", "none")
    .attr("stroke", "steelblue")
    .attr("stroke-linejoin", "round")
    .attr("stroke-linecap", "round")
    .attr("stroke-width", 1.5)
    .attr("d", line);

  var focus = g.append("g")
    .attr("class", "focus")
    .style("display", "none");

  focus.append("line")
    .attr("class", "x-hover-line hover-line")
    .attr("y1", 0)
    .attr("y2", height);

  focus.append("line")
    .attr("class", "y-hover-line hover-line")
    .attr("x1", width)
    .attr("x2", width);

  focus.append("circle")
    .attr("r", 7.5);

  focus.append("text")
    .attr("x", 15)
    .attr("dy", ".31em");

  svg.append("rect")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height)
    .on("mouseover", function() {
      focus.style("display", null);
    })
    .on("mouseout", function() {
      focus.style("display", "none");
    })
    .on("mousemove", function() { //problem in this function
      var x0 = d3.timeFormat("%Y")(x.invert(d3.mouse(this)[0])),
        i = bisectDate(data, x0, 1);
      d0 = data[i - 1],
        d1 = data[i],
        d = x0 - d0.year > d1.year - x0 ? d1 : d0;
      focus.attr("transform", "translate(" + x(d3.timeParse("%Y")(d.year)) + "," + y(d.value) + ")");
      focus.select("text").text(function() {
        return d.value;
      });
      focus.select(".x-hover-line").attr("y2", height - y(d.value));
      focus.select(".y-hover-line").attr("x2", width + width);
    });

}
drawChart_gdp(data_gdp, '.line-chart2');
<head>
  <style>
    circle {
      fill: steelblue;
    }

    body {
      font: 12px Arial;
    }

    path {
      stroke: steelblue;
      stroke-width: 2;
      fill: none;
    }

    .axis path,
    .axis line {
      fill: none;
      stroke: grey;
      stroke-width: 1;
      shape-rendering: crispEdges;
    }

    div.tooltip {
      position: absolute;
      text-align: center;
      width: 80px;
      height: 64px;
      padding: 2px;
      font: 14px sans-serif;
      color: black;
      background: lightsteelblue;
      border: 0px;
      border-radius: 8px;
      pointer-events: none;
    }

    .overlay {
      fill: none;
      pointer-events: all;
    }

    .focus circle {
      fill: #F1F3F3;
      stroke: #6F257F;
      stroke-width: 5px;
    }

    .hover-line {
      stroke: #6F257F;
      stroke-width: 2px;
      stroke-dasharray: 3, 3;
    }

  </style>

</head>

<body>
  <svg class='line-chart2'></svg>
  <script src="https://d3js.org/d3.v4.min.js"></script>
</body>

As you know by now, all this confusion comes to the fact that you have strings in your data array, but x is a time scale. My advice: parse the strings in your data array, creating date objects, and be consistent in your code, dealing all data as date objects, not strings.

Upvotes: 1

Related Questions