Connected two line using svg in javascript - javascript

I draw two line using svg, but when I render it, I only view one line and the other can't be seen. I dont know how to append that two line and make it all seen. That two line supposed to be seen when I click a polygon. Can anyone help me?
Here's my code
var group = evt.target.parentNode;
// Get the bounding box of the group
var bbox = group.getBBox();
// Add a triangle to the group
var svgns = "http://www.w3.org/2000/svg";
var line = document.createElementNS(svgns, "line");
var line2 = document.createElementNS(svgns, "line");
line.setAttribute('id','line2');
line.setAttribute('x1','0');
line.setAttribute('y1','0');
line.setAttribute('x2','5');
line.setAttribute('y2','19');
line.setAttribute("stroke", "black")
line2.setAttribute('id','line2');
line2.setAttribute('x1', '7');
line2.setAttribute('y1','5');
line2.setAttribute('x2','5');
line2.setAttribute('y2','19');
line2.setAttribute("stroke", "black");
var xPos = bbox.x + bbox.width / 2;
var yPos = bbox.y + bbox.height / 2;
line2.setAttribute("transform", "translate(" + xPos + "," + yPos + ")");
group.appendChild(line2);

I see you are appending line2 to the group.
group.appendChild(line2);
But where is the appendChild() call for the other line?

Related

D3 map, 'd' attribute

(sorry for my english bad level)
Hi I'm using D3 for the first time with mithril js. The map is ok but I have a problem with colors of provinces and it comes from the 'd' attribute to get the id of provinces.The attribute is undefined and I don't understand what is 'd' exactly. is mithril the problem? is there an other way to get 'd' attribute?
controller.map = function(el){
var width = 1160;
var height = 960;
var scale = 10000;
var offset = [width / 2, height / 2];
var center = [0, 50.64];
var rotate = [-4.668, 0];
var parallels = [51.74, 49.34];
var projection = d3.geo.albers()
.center(center)
.rotate(rotate)
.parallels(parallels)
.scale(scale)
.translate(offset)
;
var path = d3.geo.path()
.projection(projection)
;
var svg = d3.select(el).append("svg")
.attr("width",width)
.attr("height",height)
;
d3.json("belprov.json",function(error,be){
if (error) return console.error(error);
var bounds = path.bounds(topojson.feature(be, be.objects.subunits));
var hscale = scale*width / (bounds[1][0] - bounds[0][0]);
var vscale = scale*height / (bounds[1][1] - bounds[0][1]);
scale = (hscale < vscale) ? hscale : vscale;
offset = [width - (bounds[0][0] + bounds[1][0])/2,
height - (bounds[0][1] + bounds[1][1])/2];
var centroid = d3.geo.centroid(topojson.feature(be, be.objects.subunits));
center = [0, centroid[1]];
rotate = [-centroid[0],0];
projection = d3.geo.albers()
.center(center)
.rotate(rotate)
.parallels(parallels)
.scale(scale)
.translate(offset);
svg.selectAll(".province")
.data(topojson.feature(be, be.objects.provinces).features)
.enter().append("path")
.attr("class", function(d) { return "province " + d.id })
.attr("d", path)
;
})
};
The "d" attribute in a path object defines the successive coordinates of the points through which the path has to go (it also gives indication about whether the path should use bezier curves, straight lines, etc.). See some documentation here.
Be careful: in d3, d is often used as a parameter for anonymous functions representing the data currently binded to the current element. So the two are completely different things.
Here, your line
.attr("d", path)
should probably look more like
.attr("d", function(d){return d.path})
i.e., take the field path within the data elements.
You can do something like this to color diffrent paths:
//make a color scale
var color20 = d3.scale.category20();
//your code as you doing
//on making paths do
svg.selectAll(".province")
.data(topojson.feature(be, be.objects.provinces).features)
.enter().append("path")
.attr("class", function(d) { return "province " + d.id })
.style("fill", function(d){return color(d.id);})//do this to color path based on id.
.attr("d", path)

How to move a node along a vector?

I have a bunch of nodes in a circle around a centre point. I got these positions by drawing arcs first then using the arcs [X,Y] position, populated an array which was used for the positions of the nodes. Using the forcelayout from the javascript library D3.
What I want to do now, if the nodes meet a certain criteria, for example, name starts with L, move them out to the outline of a bigger circle. I have made a simple diagram to explain.
I wish to be able to move from [X2,Y2] to [X3,Y3]. I labelled [X1,Y1] as I am sure you would need this to work out the vector from x1y2 to x2,y2 wish would then be used to calculate the movement along that vector, but I'm unsure how to do this movement
I don't know if the problem is still active, but I'll answer anyway. Since the Problem has a cylindrical symmetry it is best to use polar coordinates. So x,y become r,phi whereas r = sqrt(x^2+y^2) and phi=arctan(y/x). If you want to move a point X(r,phi) in the radial direction by lets say r' you do it by simple adding it to the existing radius. Thus X'=X(r+r',phi)
Here's the way I solved it. I had a variable moveOutso I could toggle between the original node position and the one I move to. So depending on the value of moveOut I alter the scale of movement away from center.
var thisNode = circleViewNode.filter(function(d){
//console.log(d)
return d.origin != 'EquivalenceSets' && d.hasRelationship != true;
});
thisNode.each(function(d){
thisNodeSize = d.thisRadius;
});
if(!moveOut){
thisScale = innerModelRadius - thisNodeSize*1.5;
moveOut = true;
} else {
thisScale = innerModelItemRadius + (outerModelItemRadius - innerModelItemRadius)/2;
moveOut = false;
}
thisNode.each(function(d){
//console.log(d);
var centerOfCircle = [width/2,height/2]; //get center
//var centerOfCircle = [arcCenter.x, arcCenter.y];
var thisPosition = [d.x, d.y]; //get position of current node
//thisVector = [center[0]-thisPosition[0], center[1]-thisPosition[1]],
var thisVector = [thisPosition[0] - centerOfCircle[0], thisPosition[1] - centerOfCircle[1]];
var thisVectorX = thisVector[0];
var thisVectorY = thisVector[1];
var xSquared = Math.pow(thisVector[0],2);
var ySquared = Math.pow(thisVector[1],2);
var normalVector = Math.sqrt(xSquared + ySquared); //using pythagoras theorum to work out length from node pos to center
//console.log(d);
thisVectorX= thisVectorX/normalVector;
thisVectorY= thisVectorY/normalVector;
// d.x = centerOfCircle[0]+(thisVectorX*thisScale);// + -38.5;
// d.y = centerOfCircle[1]+(thisVectorY*thisScale);// + -20;
d.x = centerOfCircle[0]+(thisVectorX*thisScale); //thisScale gives the ability to move back to original pos
d.y = centerOfCircle[1]+(thisVectorY*thisScale);
//}
})
.transition().duration(1000)
.attr("transform", function(d)
{
//console.log(d.hasRelationship);
//console.log(d.y);
return "translate(" + d.x + "," + d.y + ")"; //transition nodes
});

D3 centre point of a line (not path)

are a few questions on here and on the D3 site about how you'd find the centre point (or any point) along a path, however I can't seem to find how to do it with a line.
I've done a simple jsfiddle here. Essentially I need to add a shape (using text in the jsfiddle to make it clearer) at a point along a line (lets say the middle for simplicity)
So I have a svg:
var canvas = d3.select('body').append('svg').attr('width', 500).attr('height', 500);
And add a line (the position is fixed and doesnt come from data)
var line = canvas.append('line').attr('x1', 50).attr('y1', 50).attr('x2', 250).attr('y2', 150);
The I add some text just to demo to the top and bottom of that line
canvas.append('text').attr('x', line.attr('x1')).attr('y', line.attr('y1')).text('top');
canvas.append('text').attr('x', line.attr('x2')).attr('y', line.attr('y2')).text('bottom');
path's have methods to get the centre point and width/BBox etc, but line doesnt seem to.
Anyone have any ideas how this can be achieved?
My initial though was to just get the difference between the x1/x2 values, like this:
canvas.append('text')
.attr('x', parseInt(line.attr('x2') - line.attr('x1')))
.attr('y', parseInt(line.attr('y2') - line.attr('y1')))
.text('just looks a bit off');
But as you'll see from the jsfiddle, it's just off somehow.
Anyone want to point out my mistake?
I guess, this will work:
var lineData = {x1: 50, y1: 50, x2: 250, y2: 150};
var canvas = d3.select('body').append('svg').attr('width', 500).attr('height', 500);
var line = canvas.append('line').attr('x1', lineData.x1).attr('y1', lineData.y1).attr('x2', lineData.x2).attr('y2', lineData.y2);
console.log(line);
var x = lineData.x1 + Math.abs(lineData.x2 - lineData.x1) / 2;
var y = lineData.y1 + Math.abs(lineData.y2 - lineData.y1) / 2;
console.log([x,y]);
canvas.append('text').attr('x', x).attr('y', y).text('X');
Line
Use simple mathematics, distance formula.
var canvas = d3.select('body').append('svg').attr('width', 500).attr('height', 500);
var line = canvas.append('line').attr('x1', 50).attr('y1', 50).attr('x2', 250).attr('y2', 150);
var x1 = parseInt(line.attr("x1"));
var y1 = parseInt(line.attr("y1"));
var x2 = parseInt(line.attr("x2"));
var y2 = parseInt(line.attr("y2"));
var midPoint = { x: (x1+x2)/2, y: (y1+y2)/2 };
canvas.append('text').attr('x', midPoint.x).attr('y', midPoint.y).text('X');
line{
stroke:#444;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Path
var canvas = d3.select('body').append('svg').attr('width', 500).attr('height', 500);
var line = canvas.append('path').attr('d', "M 50 50 L 250 150");
var path = line.node();
var midPoint = path.getPointAtLength(path.getTotalLength()/2);
canvas.append('text').attr('x', midPoint.x).attr('y', midPoint.y).text('X');
path{
stroke: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Resolved the issue so thought I'd answer it incase anyone else found the question.
Used getBBox() of the node (using .node()). Used that to get the width, height and xy:
var canvas = d3.select('body').append('svg')
.attr('width', 500)
.attr('height', 500);
var line = canvas.append('line')
.attr('x1', 50)
.attr('y1', 150)
.attr('x2', 50)
.attr('y2', 250);
Then the middle x and y are:
var midX = line.node().getBBox().x + line.node().getBBox().width / 2;
var midY = line.node().getBBox().y + line.node().getBBox().height / 2;

placing d3 label at end of arc

Given the following speed dial, which is constructed using arcs in D3:
segmentArc = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth).startAngle(arcStartRad + startPadRad).endAngle(arcEndRad - endPadRad);
How do I move the labels in each segment so that it appears right justified (at the end of each segment opposed to center)?
the labels are currently added likes this:
chart.append('text')
.attr('transform', () => {
var x = Math.round(segmentArc.centroid()[0]);
var y = Math.round(segmentArc.centroid()[1]);
return 'translate(' + x + ',' + y + ')';
})
.style("text-anchor", "middle")
.text(sectionLabel);
I solved this problem by replicating each segment arc, giving it a transparent fill and making it exactly twice as long. From there it is as simple as working out the centroid for the transparent arc.

Updating Text Labels in D3JS

I have been struggling with this issue for the past couple days: I have a force directed graph that labels its edges just like this example does it. The problem I am facing is that when the graph updates (ie: a node on the graph is added upon a user's click) it updates the graph but it leaves the old edge labels that I wrote previously behind:
BEFORE & AFTER A NEW GRAPH IS APPENDED:
As you can see, my edge labels are hanging around after an update. I have a function that is called everytime new data comes in, and in this function I have the following code that draws the labels:
path_text = svg.selectAll(".path")
.data(force.links(), function(d){ return d.name;})
.enter().append("svg:g");
path_text.append("svg:text")
.attr("class","path-text")
.text(function(d) { return d.data.label; });
The svg variable is declared once at a top level closure like so:
var svg = d3.select("body").append("svg:svg")
.attr("viewBox", "0 0 " + width + " " + height)
.attr("preserveAspectRatio", "xMidYMid meet");
My graph has a tick() function that calculates the location of each label like so:
function tick()
{
// Line label
path_text.attr("transform", function(d)
{
var dx = (d.target.x - d.source.x),
dy = (d.target.y - d.source.y);
var dr = Math.sqrt(dx * dx + dy * dy);
var sinus = dy/dr;
var cosinus = dx/dr;
var l = d.data.label.length * 6;
var offset = (1 - (l / dr )) / 2;
var x=(d.source.x + dx*offset);
var y=(d.source.y + dy*offset);
return "translate(" + x + "," + y + ") matrix("+cosinus+", "+sinus+",
"+-sinus+", "+cosinus+", 0 , 0)";
});
.
.
.
I have tried moving this svg declaration down into the update function, so that this is instantiated each time there is a graph change. This actually works - but it makes an entire duplicate of the entire graph. The first, original copy still keeps the old labels - but the second copy acts exactly how I want it to. Is there a way, perhaps, instead of appending svg, there is a way of replacing? I have also tried calling exit().remove() without any luck as well.
Thank you so much for your time. This has been killing me as to how I'm supposed to do this.
I placed the svg declaration inside my graph update function, attached it to a div, and clear the div before appending it again:
jQuery('#v').empty();
var svg = d3.select("#v").append("svg:svg")
.attr("viewBox", "0 0 " + width + " " + height)
.attr("preserveAspectRatio", "xMidYMid meet");
Not the cleanest solution in my opinion, but will go with this unless you all have a better solution!

Categories

Resources