akshaivk
akshaivk

Reputation: 427

d3.js - Multi series line chart tool tip issue

I am new to d3.js got the following code from stack overflow. I had customized it according to my needs. But when adding tool tip it is not moving according to the mouse movement and also only date is showing on top of y axis. I need to display tool tip in the order Date: date -newline- OP: some value -newline- IP: some value -newline- Pharmacy: some value -newline- Total: OP+IP+Pharmacy where mouse is hover on the line.

TSV file and code is as follows. Thanks in advance.

html file

</!DOCTYPE html>
<html>
<head>

<style>

body {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}


.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 1.5px;
}

</style>
 <script type="text/javascript" src="assets/js/plugins/visualization/d3/d3.min.js"></script>
</head>
<body>
<div id="revenueStati"></div>
<script>
linchart();
function linchart(){
var margin = {top: 20, right: 80, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var parseDate = d3.time.format("%Y%m%d").parse;
var formatDate = d3.time.format("%d-%b")
 var bisectDate = d3.bisector(function(d) { return d.date; }).left
var x = d3.time.scale()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height-10, 0]);

var color = d3.scale.category10();

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom").ticks(7);

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var line = d3.svg.line()
    .interpolate("basis")
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.temperature); });

var svg = d3.select("#revenueStati").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");





d3.tsv("data.tsv", function(error, data) {
  if (error) throw error;

  color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));

  data.forEach(function(d) {
    d.date = parseDate(d.date);
  });

  var cities = color.domain().map(function(name) {
    return {
      name: name,
      values: data.map(function(d) {
        return {date: d.date, temperature: +d[name]};
      })
    };
  });

  x.domain(d3.extent(data, function(d) { return d.date; }));

  y.domain([
    d3.min(cities, function(c) { return d3.min(c.values, function(v) { return v.temperature; }); }),
    d3.max(cities, function(c) { return d3.max(c.values, function(v) { return v.temperature; }); })
  ]);

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end");

  var city = svg.selectAll(".city")
      .data(cities)
    .enter().append("g")
      .attr("class", "city");

  city.append("path")
      .attr("class", "line")
      .attr("d", function(d) { return line(d.values); })
      .style("stroke", function(d) { return color(d.name); });

  city.append("text")
      .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
      .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; })
      .attr("x", 3)
      .attr("dy", ".35em")
      .text(function(d) { return d.name; });

      //// - tooltip


city = svg.append("g") 
    .style("display", "none");
      // append the x line
    city.append("line")
        .attr("class", "x")
        .style("stroke", "blue")
        .style("stroke-dasharray", "3,3")
        .style("opacity", 0.5)
        .attr("y1", 0)
        .attr("y2", height);

    // append the y line
    city.append("line")
        .attr("class", "y")
        .style("stroke", "blue")
        .style("stroke-dasharray", "3,3")
        .style("opacity", 0.5)
        .attr("x1", width)
        .attr("x2", width);

    // append the circle at the intersection
    city.append("circle")
        .attr("class", "y")
        .style("fill", "none")
        .style("stroke", "blue")
        .attr("r", 4);

    // place the value at the intersection
    city.append("text")
        .attr("class", "y1")
        .style("stroke", "white")
        .style("stroke-width", "3.5px")
        .style("opacity", 0.8)
        .attr("dx", 8)
        .attr("dy", "-.3em");
    city.append("text")
        .attr("class", "y2")
        .attr("dx", 8)
        .attr("dy", "-.3em");

    // place the date at the intersection
    city.append("text")
        .attr("class", "y3")
        .style("stroke", "white")
        .style("stroke-width", "3.5px")
        .style("opacity", 0.8)
        .attr("dx", 8)
        .attr("dy", "1em");
    city.append("text")
        .attr("class", "y4")
        .attr("dx", 8)
        .attr("dy", "1em");

    // append the rectangle to capture mouse
    svg.append("rect")
        .attr("width", width)
        .attr("height", height)
        .style("fill", "none")
        .style("pointer-events", "all")
        .on("mouseover", function() { city.style("display", null); })
        .on("mouseout", function() { city.style("display", "none"); })
        .on("mousemove", mousemove);

    function mousemove() {
    var x0 = x.invert(d3.mouse(this)[0]),
        i = bisectDate(data, x0, 1),
        d0 = data[i - 1],
        d1 = data[i],
        d = x0 - d0.date > d1.date - x0 ? d1 : d0;

    city.select("circle.y")
        .attr("transform",
              "translate(" + x(d.date) + "," +
                             y(d.close) + ")");

    city.select("text.y1")
        .attr("transform",
              "translate(" + x(d.date) + "," +
                             y(d.close) + ")")
        .text(d.close);

    city.select("text.y2")
        .attr("transform",
              "translate(" + x(d.date) + "," +
                             y(d.close) + ")")
        .text(d.close);

    city.select("text.y3")
        .attr("transform",
              "translate(" + x(d.date) + "," +
                             y(d.close) + ")")
        .text(formatDate(d.date));

    city.select("text.y4")
        .attr("transform",
              "translate(" + x(d.date) + "," +
                             y(d.close) + ")")
        .text(formatDate(d.date));

    city.select(".x")
        .attr("transform",
              "translate(" + x(d.date) + "," +
                             y(d.close) + ")")
                   .attr("y2", height - y(d.close));

    city.select(".y")
        .attr("transform",
              "translate(" + width * -1 + "," +
                             y(d.close) + ")")
                   .attr("x2", width + width);
  }

});
}

</script>
</body>

</html>

data.tsv file

date    OP  IP  Pharmacy
20160406    46905.00    10360.00    52558.00
20160407    45415.00    10910.00    46665.00
20160408    69770.00    10935.00    46377.00
20160409    58455.00    29900.00    37352.00
20160410    10345.00    7200.00 22971.00
20160411    48680.00    14535.00    46482.00
20160412    42452.00    16270.00    34859.00

demo graph

Upvotes: 0

Views: 189

Answers (1)

Klaujesi
Klaujesi

Reputation: 1836

You code is take from examples with One data serie and you have multiple series:

The important start here:

var x0 = x.invert(d3.mouse(this)[0]),
    i = bisectDate(data, x0, 1),
    d0 = data[i - 1],
    d1 = data[i],
    d = x0 - d0.date > d1.date - x0 ? d1 : d0;

city.select("circle.y")
    .attr("transform",
          "translate(" + x(d.date) + "," +  // based on date
                         y(d.close) + ")"); // you need find y value 
                                            // d.close is not defined

You have 3 serie. You could take the max value at some date and draw the circle there:

var ymax = d3.max([+d["OP"],+d["IP"],+d["Pharmacy"]])
var xm = x(d.date);
var ym = y(ymax);

city.select("circle.y")
    .attr("transform",
          "translate(" + xm + "," +
                         ym + ")");

Or, draw three circles, one for each series:

city.select("circle.y")
    .attr("transform",
          "translate(" + xm + "," +
                         y(+d["OP"]) + ")");

city.select("circle.y")
    .attr("transform",
          "translate(" + xm + "," +
                         y(+d["IP"]) + ")");

city.select("circle.y")
    .attr("transform",
          "translate(" + xm + "," +
                         y(+d["Pharmacy"]) + ")");

Now you know how to calulate (x,y) you can do the horizontal & vertical dashed lines with labels.

Here's s psudoworking code. I've let you complete it as homework :)

Upvotes: 2

Related Questions