In d3, come ottenere i dati della linea interpolati da una linea SVG?

Visualizzo un grafico a linee con D3 con approssimativamente il seguente codice (date le funzioni di scala x , y i data dell’array float):

  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)); 

Ora voglio conoscere l’ altezza verticale della linea in una determinata posizione in pixel orizzontale . L’array di data ha meno punti di dati rispetto ai pixel e la linea visualizzata viene interpolata , quindi non è semplice dedurre l’altezza della linea a un dato pixel solo dall’array di data .

Qualche suggerimento?

Modificato il 19 settembre 2012 per commenti con molte grazie a nrabinowitz!

Dovrai fare una sorta di ricerca dei dati restituiti da getPointAtLength . (Vedi 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"))); 

Per me questo ritorna [5.000403881072998, 140.6229248046875].

Questa funzione di ricerca, findYatX , è tutt'altro che efficiente (viene eseguita in tempo O (n) ), ma illustra il punto.

Questa soluzione è molto più efficiente della risposta accettata. Il suo tempo di esecuzione è logaritmico (mentre la risposta accettata ha una complessità lineare).

 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 } 

Ho provato a implementare findYatXbisection (come suggerito da bumbu), e non ho potuto farlo funzionare COSÌ COM’È.

Invece di modificare la lunghezza in funzione di length_end e length_start, ho appena diminuito la lunghezza del 50% (se x point.x) ma sempre rispetto alla lunghezza iniziale di zero . Ho anche incorporato revXscale / revYscale per convertire i pixel in valori x / y come impostato dalle mie funzioni d3.scale.

 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) }