简体   繁体   中英

In d3, how to get the interpolated line data from a SVG line?

I display a line chart with D3 with roughly the following code (given the scale functions x , y and the float array data ):

 var line = d3.svg.line()
         .interpolate("basis")
         .x(function (d, i) { return x(i); })
         .y(function (d) { return y(d); });
 d3.select('.line').attr('d', line(data));

Now I want to know the vertical height of the line at a given horizontal pixel position . The data array has lesser data points than pixels and the displayed line is interpolated , so it is not straight-forward to deduce the height of the line at a given pixel just from the data array.

Any hints?

This solution is much more efficient than the accepted answer. It's execution time is logarithmic (while accepted answer has linear complexity).

var findYatXbyBisection = function(x, path, error){
  var length_end = path.getTotalLength()
    , length_start = 0
    , point = path.getPointAtLength((length_end + length_start) / 2) // get the middle point
    , bisection_iterations_max = 50
    , bisection_iterations = 0

  error = error || 0.01

  while (x < point.x - error || x > point.x + error) {
    // get the middle point
    point = path.getPointAtLength((length_end + length_start) / 2)

    if (x < point.x) {
      length_end = (length_start + length_end)/2
    } else {
      length_start = (length_start + length_end)/2
    }

    // Increase iteration
    if(bisection_iterations_max < ++ bisection_iterations)
      break;
  }
  return point.y
}

Edited 19-Sep-2012 per comments with many thanks to nrabinowitz!

You will need to do some sort of search of the data returned by getPointAtLength . (See https://developer.mozilla.org/en-US/docs/DOM/SVGPathElement .)

// Line
var line = d3.svg.line()
     .interpolate("basis")
     .x(function (d) { return i; })
     .y(function(d, i) { return 100*Math.sin(i) + 100; });

// Append the path to the DOM
d3.select("svg#chart") //or whatever your SVG container is
     .append("svg:path")
     .attr("d", line([0,10,20,30,40,50,60,70,80,90,100]))
     .attr("id", "myline");

// Get the coordinates
function findYatX(x, linePath) {
     function getXY(len) {
          var point = linePath.getPointAtLength(len);
          return [point.x, point.y];
     }
     var curlen = 0;
     while (getXY(curlen)[0] < x) { curlen += 0.01; }
     return getXY(curlen);
}

console.log(findYatX(5, document.getElementById("myline")));

For me this returns [5.000403881072998, 140.6229248046875].

This search function, findYatX , is far from efficient (runs in O(n) time), but illustrates the point.

I have tried implementing findYatXbisection (as nicely suggested by bumbu), and I could not get it to work AS IS.

Instead of modifying the length as a function of length_end and length_start, I just decreased the length by 50% (if x < point.x) or increased by 50% (if x> point.x) but always relative to start length of zero. I have also incorporated revXscale/revYscale to convert pixels to x/y values as set by my d3.scale functions.

function findYatX(x,path,error){
    var length = apath.getTotalLength()
        , point = path.getPointAtLength(length)
        , bisection_iterations_max=50
        , bisection_iterations = 0
    error = error || 0.1
    while (x < revXscale(point.x) -error || x> revXscale(point.x + error) {
        point = path.getPointAtlength(length)
        if (x < revXscale(point.x)) {
             length = length/2
        } else {
             length = 3/2*length
        }
        if (bisection_iterations_max < ++ bisection_iterations) {
              break;
        }
    }
return revYscale(point.y)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM