When I can create a line as follows:
var lineData = [{ "x": 50, "y": 50 }, {"x": 100,"y": 100}, {"x": 150,"y": 150}, {"x": 200, "y": 200}];
var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("basis");
var myLine = lineEnter.append("path")
.attr("d", lineFunction(lineData))
Now I want to add a text to the second point of this lineArray:
lineEnter.append("text").text("Yaprak").attr("y", function(d){
console.log(d); // This is null
console.log("MyLine");
console.log(myLine.attr("d")) // This is the string given below, unfortunately as a String
// return lineData[1].x
return 10;
} );
Output of the line console.log(myLine.attr("d"))
:
M50,50L58.33333333333332,58.33333333333332C66.66666666666666,66.66666666666666,83.33333333333331,83.33333333333331,99.99999999999999,99.99999999999999C116.66666666666666,116.66666666666666,133.33333333333331,133.33333333333331,150,150C166.66666666666666,166.66666666666666,183.33333333333331,183.33333333333331,191.66666666666663,191.66666666666663L200,200
I can get the path data in string format. Can I convert this data back to lineData array? Or, is there any other and simple way to regenerate or get the lineData when appending a text?
Please refer to this JSFiddle.
A little hacky, but you can use animateMotion to animate an object (e.g. a rect or a circle) along the path and then sample the x/y position of the object. You will have to make a bunch of choices (e.g. how fast do you animated the object, how fast do you sample the x/y position, etc.). You could also run this process multiple times and take some kind of average or median.
Full code (see it in action: http://jsfiddle.net/mqmkc7xz/)
I found this question by Google. What I needed was simply the
pathSegList
property of a SVGpath
object:Every point looks like
See
You can break the line into individual commands by splitting the string on the
L
,M
, andC
characters:This gives the sequence of commands that are used to render the path. Each will be a string comprised of a character (L, M, or C) followed by a bunch of numbers separated by commas. They will look something like this:
That describes a curve through three points, [66,66], [83,83], and [99,99]. You can process these into arrays of pairs points with another
split
command and a loop, contained in a map:This will return an array containing each command as an array of length-2 arrays, each of which is an (x,y) coordinate pair for a point in the corresponding part of the path.
You could also modify the function in
map
to return object that contain both the command type and the points in the array.EDIT: If you want to be able to access
lineData
, you can add it as data to a group, and then append the path to the group, and the text to the group.This would be a more d3-esque way of accessing the data than reverse-engineering the path. Also probably more understandable.
More info on SVG path elements
pathSegList
is supported in old Chrome and is removed since Chrome 48.But Chrome has not implemented the new API.
Use path seg polyfill to work with old API.
Use path data polyfill to work with new API. It's recommended.
The
SVGPathElement
API has built-in methods for getting this info. You do not need to parse the data-string yourself.Since you stored a selection for your line as a variable, you can easily access the path element's api using
myLine.node()
to refer to thepath
element itself.For example:
Then you can access the list of commands used to construct the path by accessing the
pathSegList
property:Using the
length
property of this object, you can easily loop through it to get the coordinates associated with each path segment:Inspecting the console output, you will find that each path segment has properties for
x
andy
representing the endpoint of that segment. For bezier curves, arcs, and the like, the control points are also given asx1
,y1
,x2
, andy2
as necessary.In your case, regardless of whether you use this method or choose to parse the string yourself, you will run into difficulties because you used
interpolate('basis')
for your line interpolation. Therefore, the line generator outputs 6 commands (in your specific case) rather than 4, and their endpoints do not always correspond to the original points in the data. If you useinterpolate('linear')
you will be able to reconstruct the original dataset, since the linear interpolation has a one-to-one correspondence with the path data output.Assuming you used linear interpolation, reconstructing the original dataset could be done as follows:
EDIT:
As far as using the original data when appending text... I'm assuming you are looking to append labels to the points, there's no need to go through all the trouble of reconstructing the data. In fact the real issue is that you never used data-binding in the first place to make your line graph. Try binding the data using the
.datum()
method for your path, and using the.data()
method for the labels. Also you might want to renamelineEnter
since you're not using an enter selection and it simply represents a group. For example: