Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flot chart - how to fire event on line hover

I have written a plugin for jQuery Flot charts which allows you to dynamically add data points by clicking on the line of a chart, remove them by right clicking and also it allows the dragging of these points around the canvas.

This works fine and I also have a tooltip displaying the Y value when you hover over or drag a point around.

What I would like to do is display a second tooltip when a user hovers over a line showing the message "Left-click to add a data point".

I can't seem to find a way to add a hover event to the line itself and there doesn't appear to be a native method.

Does anyone know how I might go about achieving this?

Thank you.

EDIT: here is a jsFiddle that includes the tooltip creation code that I am using:

jsFiddle

as you can see a tooltip renders when you hover over an actual datapoint, however I would like to find a way to fire have a seperate tooltip rendered when you hover over the line inbetween the datapoints. NOTE: this fiddle does NOT include my custom code to add and drag the datapoints dynamically as it would be too much code for the purposes of this question.

like image 297
gordyr Avatar asked Feb 14 '12 17:02

gordyr


1 Answers

So basically we are looking to conditionally display a tooltip when the position of the cursor meets the requirement that it is over a line on the chart. Since lines are not an entity we can work with you need calculate the line between the nearest two points on either side of your cursor and then see if your current position lies on it. I simplified your example a bit:

Function to calculate distance between two points:

function lineDistance( p1x, p1y,p2x, p2y ) {
    return Math.sqrt( (p2x - p1x)*(p2x - p1x) + (p2y-p1y)*(p2y-p1y) );
}

Assuming that your nearest two points are A and B to your cursor C then the distance AB should equal AC + BC.

So to determine if it is on the line: Math.abs(AB-(AC+BC)) < SomeThreshold. Using the threshold essentially draws a box around the line which the cursor can fall inside of.

Then extending your code in the plothover (jsFiddle)

$(placeholder).bind("plothover", function (event, pos, item) {
    if (item) {
        var tipText;

        if (opts.xaxis.mode === "time" || opts.xaxes[0].mode === "time") {
            tipText = stringFormat(to.content, item, timestampToDate);
        } else {
            tipText = stringFormat(to.content, item);
        }

        $tip.html(tipText).css({
            left: tipPosition.x + to.shifts.x,
            top: tipPosition.y + to.shifts.y
            }).show();
    } else {
         // Extended for line hover
         var series = plot.getData();
         var xBeforeIndex = 0;
         var xAfterIndex = -1;
         var Threshold = 0.0000025;
         var i = 1;
         while (i <= series[0].data.length && xAfterIndex==-1) {
             if (xAfterIndex == -1 && pos.x > series[0].data[i][0]) {
                 xBeforeIndex = i;
             } else if (xAfterIndex == -1) {
                 xAfterIndex = i;
             }
             i++;
         }

         var onTheLine = 
             lineDistance(
                series[0].data[xBeforeIndex][0]/10000,series[0].data[xBeforeIndex][1], 
                pos.x/10000, pos.y)
             +lineDistance(pos.x/10000, pos.y,
                series[0].data[xAfterIndex][0]/10000,series[0].data[xAfterIndex][1])
             -lineDistance(
                series[0].data[xBeforeIndex][0]/10000,series[0].data[xBeforeIndex][1],
                series[0].data[xAfterIndex][0]/10000,series[0].data[xAfterIndex][1]);

          if (Math.abs(onTheLine) < Threshold) {
              tipText = "Found Line";
              $tip.html(tipText).css({
                  left: tipPosition.x + to.shifts.x,
                  top: tipPosition.y + to.shifts.y
                  }).show();
           } else {
               $tip.hide().html('');
           }
       }
  });

Things not done here:

  1. Check your Edge cases more appropriately - the above assumes the first and last points are on the edges of the graph.
  2. Add back in your second graph
  3. Improve the data set searching performance using an approach like bubblesort to find the before/after indexes.
  4. Note that I am scaling down the x-axis by 10000. The numbers were too big and in the big gap between the first two points made the y-axis differences insignificant (the result was always zero between these two points).

Note, adding the second graph will require you to find the nearest points for both graphs and checking if it falls on either line. If your lines are close or intersecting you could just make one the priority line. If you struggle adding the second line back in I can help later.

like image 124
Matthew Avatar answered Oct 17 '22 07:10

Matthew