Creating line chart with d3 getting error - javascript

In the following example I am using a lineFunction for plotting co-ordinates for the path. I hope to enter code correctly, still facing error. Can someone help?
SNIPPET:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
<script>
$(document).ready(function(){
//our basic data
var customData = [
{"x": 100, "y": 100},
{"x": 200, "y": 50},
{"x": 300, "y": 150},
{"x": 400, "y": 20}
];
//the line function for path
var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");
//Main svg container
var mySVG = d3.select("svg");
//defining the lines
var path = mySVG.append("path");
//plotting lines
path
.attr("d", lineFunction(customData))
.attr("stroke",function() { return "hsl(" + Math.random() * 360 + ",100%,50%)"; })
.attr("stroke-width", 2)
.attr("fill", "none");
});
</script>
</head>
<body>
<svg width="500px" height="500px"></svg>
</body>
</html>
ERROR:
var lineFunction = d3.svg.line() //error here ...

Looks like versioning issue for me take look at this -
$(document).ready(function(){
//our basic data
var customData = [
{"x": 100, "y": 100},
{"x": 200, "y": 50},
{"x": 300, "y": 150},
{"x": 400, "y": 20}
];
//the line function for path
var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");
//Main svg container
var mySVG = d3.select("svg");
//defining the lines
var path = mySVG.append("path");
//plotting lines
path
.attr("d", lineFunction(customData))
.attr("stroke",function() { return "hsl(" + Math.random() * 360 + ",100%,50%)"; })
.attr("stroke-width", 2)
.attr("fill", "none");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="//d3js.org/d3.v3.min.js"></script></script>
<svg width="500px" height="500px"></svg>

As Chetan already mentioned, it's a version problem of your included D3 file. You are including D3 V4 but using the syntax of D3 V3.
Within V4 they removed d3.svg. So everything you have to do is to change d3.svg.line() to d3.line() and it should work.
Ref: d3.svg.line() error: Uncaught TypeError: Cannot read property 'line' of undefined
EDIT-1: try this code Snippet
var svg;
//The data for our line
var lineData = [ { "x": 1, "y": 5}, { "x": 20, "y": 20},
{ "x": 40, "y": 10}, { "x": 60, "y": 40},
{ "x": 80, "y": 5}, { "x": 100, "y": 60}];
//This is the accessor function we talked about above
var lineFunction = d3.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
//The SVG Container
var svgContainer = d3.select("body").append("svg:svg")
.attr("width", 200)
.attr("height", 200);
//The line SVG Path we draw
var lineGraph = svgContainer.append("path")
.attr("d", lineFunction(lineData))
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "none");

Related

Plotting nodes and links from JSON files with SVG

I'm new to D3 and I'm having trouble visualising my json file. I'm supposed to plot the locations (sites) as circles with their radius equal to the "amount". I'm extremely confused about how to work with the nodes and links. I have provided an example of the JSON code as well. Please help me understand where I am going wrong with the coding
<html>
<head>
<title>D3 Visualisation </title>
<h1> Trading Data </h1>
<style>
.svgCanvas {
border: solid 1px
}
</style>
</head>
<body>
<svg></svg>
<script src="https://d3js.org/d3.v4.min.js"></script> <script>
window.onload = function(){
var width = 800;
var height = 300;
var thisCanvas = d3.select("svg")
.attr("width", width)
.attr("height", height)
.attr("class", "svgCanvas");
d3.json("data.json", function(d){
console.log(d);
var svgCanvas.selectAll("circle")
.data(d).enter()
.append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return d.amount; } )
.style("fill", “lightgreen”); })
})
}
</script>
</body>
</html>
The Json code example is as follows:
{
"nodes": [
{
"id": "site09",
"x": 317.5,
"y": 282.5
},
{
"id": "site01",
"x": 112,
"y": 47
},
{
"id": "site03",
"x": 69.5,
"y": 287
}
],
"links": [
{"node01": "site05", "node02": "site08", "amount": 10},
{"node01": "site05", "node02": "site02", "amount": 120},
{"node01": "site05", "node02": "site03", "amount": 50}
]
}
Here you can get the working solution. Just replace the data object with json.
https://playcode.io/324480?tabs=console&script.js&output

Position of node changes when they link (using d3.js)

I am a newbie in d3.js. I have tried to create a static architecture with 5 nodes and link them with each other according to preferences, the nodes should be organized like so:
At the beginning I set the position of the nodes and then create the links. Though, when the nodes get linked, the architecture changes and the result is the one displayed below:
Here is my code:
var width = 640,
height = 400;
var nodes = [
{ x: 60, y: 0, id: 0},
{ x: 150, y: height/4, id: 1},
{ x: 220, y: height/4, id: 2},
{ x: 340, y: height/4, id: 3},
{ x: 420, y: height/2, id: 4},
{ x: 480, y: height/2, id: 5}
];
var links = [
{ source: 1, target: 5 },
{ source: 0, target: 5 },
{ source: 2, target: 1 },
{ source: 3, target: 2 },
{ source: 4, target: 5 }
];
var graph = d3.select('#graph');
var svg = graph.append('svg')
.attr('width', width)
.attr('height', height);
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
.links(links);
force.linkDistance(width/2);
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 1e-6);
var node = svg.selectAll('.node')
.data(nodes)
.enter().append("circle")
.attr("cx", d=> d.x)
.attr("cy", d=> d.y)
.attr('class', 'node')
.on("mouseover", function(d){
d3.select(this)
.transition()
.duration(500)
.style("cursor", "pointer")
div
.transition()
.duration(300)
.style("opacity", "1")
.style("display", "block")
console.log("label", d.label);
div
.html("IP: " + d.label + " x: " + d.x + " y: " + d.y)
.style("left", (d3.event.pageX ) + "px")
.style("top", (d3.event.pageY) + "px");
})
.on("mouseout", mouseout);
function mouseout() {
div.transition()
.duration(300)
.style("opacity", "0")
}
console.log("wait...");
force.on('end', function() {
node.attr('r', width/25)
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; });
link.attr('x1', function(d) { console.log("LINE x1-> ", d.source.x); return d.source.x; })
.attr('y1', function(d) { console.log("LINE y1-> ", d.source.y); return d.source.y; })
.attr('x2', function(d) { console.log("LINE x2-> ", d.source.x); return d.target.x; })
.attr('y2', function(d) { console.log("LINE y2-> ", d.source.y); return d.target.y; })
.attr("stroke-width", 2)
.attr("stroke","black");
});
force.start();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="graph"></div>
Could you please help me?
Thank you in advance.
A force layout offers some advantages that derive from its nature as a self organizing layout:
It places nodes and links automatically avoiding manual positioning of potentially thousands of elements
It organizes nodes and links based on assigned forces to an ideal spacing and layout
You have nodes to which you have already assigned positions, the two advantages listed above do not apply. You've already manually done the first item, and the second item will disturb and overwrite the positions you manually set.
We could fix the node positions, but if we do this with all nodes, it defeats the purpose of the force layout: to position nodes by simulating forces.
Instead, if you have the position of all nodes, we can skip the force and just append everything based on the data. The snippet below places the links first (so they are behind the nodes) using the index contained in d.source/d.target to access the specific node in the nodes array and get the appropriate x or y coordinate. The nodes are positioned normally.
It appears you have adjusted the code to use circles in your question though the screenshot uses images (as you also used in a previous question), I'll just use circles here. Based on the coordinates you've given some lines overlap. I modified the first node so that the y value wasn't 0 (which would have pushed half the circle off the svg)
var width = 640,
height = 400;
var nodes = [
{ x: 60, y: height/8, id: 0},
{ x: 150, y: height/4, id: 1},
{ x: 220, y: height/4, id: 2},
{ x: 340, y: height/4, id: 3},
{ x: 420, y: height/2, id: 4},
{ x: 480, y: height/2, id: 5}
];
var links = [
{ source: 1, target: 5 },
{ source: 0, target: 5 },
{ source: 2, target: 1 },
{ source: 3, target: 2 },
{ source: 4, target: 5 }
];
var graph = d3.select('#graph');
var svg = graph.append('svg')
.attr('width', width)
.attr('height', height);
// append links:
svg.selectAll()
.data(links)
.enter()
.append("line")
.attr("x1", function(d) { return nodes[d.source].x; })
.attr("y1", function(d) { return nodes[d.source].y; })
.attr("x2", function(d) { return nodes[d.target].x; })
.attr("y2", function(d) { return nodes[d.target].y; })
.attr("stroke-width", 2)
.attr("stroke","black");
// append nodes:
svg.selectAll()
.data(nodes)
.enter()
.append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 8);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="graph"></div>

d3js force button to allow/disable drag actions

My goal is to allow or disable the drag action on the nodes of my graph. I have this input : <input id="drag" name="drag" type="button" value="switch drag" />. I found some hints here and on this question.
As I said in a comment below, it doesn't work if I create nodes and links from an external json file. Is my function d3.json("graph.json", function(error, graph) {...}); not correct ?
Here's my json file:
{
"nodes": [
{"x": 260, "y": 210, "fixed": true},
{"x": 300, "y": 210, "fixed": true},
{"x": 260, "y": 260, "fixed": true},
{"x": 300, "y": 260, "fixed": true}
],
"links": [
{"source": 0, "target": 1},
{"source": 0, "target": 2},
{"source": 2, "target": 3},
{"source": 3, "target": 1}
]
}
And here's my code :
var width = 960, height = 500;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var myLinks = svg.selectAll(".link");
var myNodes = svg.selectAll(".node");
var force = d3.layout.force()
.size([width, height])
.charge(-400)
.linkDistance(40)
.on("tick", tick);
// Allows the drag actions
var drag = force.drag();
// Read the json file and creates the links and the nodes
d3.json("graph.json", function(error, graph) {
if (error) alert(error);
force
.nodes(graph.nodes)
.links(graph.links)
.start();
myLinks = myLinks.data(graph.links)
.enter()
.append("line")
.attr("class", "link");
myNodes = myNodes.data(graph.nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("r", 6)
.call(drag);
});
// Add properties to myLinks and myNodes
function tick() {
myLinks.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
myNodes.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
var dragOnOff = d3.select("#drag");
dragOnOff.on("click", function() {
myNodes.on('mousedown.drag', null);
});
var dragOnOff = d3.select("#drag");
var dragCallback = myNodes.property('__onmousedown.drag')['_'];
var draggable = true;
dragOnOff.on("click", function () {
myNodes.on('mousedown.drag', draggable ? null : dragCallback);
this.value = 'switch drag to ' + draggable;
draggable = !draggable;
});
I wish someone could answer me and my post can help someone else !
Thanks in advance!
Use on "click" for buttons instead of on "input".
var dragOnOff = d3.select('#drag');
var draggable = true;
dragOnOff.on('click', function () {
myNodes.on('mousedown.drag', draggable ? null : dragCallback);
draggable = !draggable;
});
http://jsfiddle.net/77yndu1e/2/

d3.js force layout drag stops working after deleting a node

I've seen this answer and this too, but they don't work.
My code is on Fiddle.
Two questions:
1. On clicking a node and pressing the delete button on the keyboard the node and corresponding links get deleted, but why am I not able to drag the remaining nodes afterward?
2. I tried attaching an image (using the path in the nodes array), but the image doesn't appear. The circles just disappear and no image appears (the path to the image is correct. In the same program I tried displaying the image at a corner of the screen and it worked).
The code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.background { /*stroke: gray; stroke-width: 1px; fill: rgb(252,231,216);*/ cursor: move; }
.link { stroke: #000; stroke-width: 1.5px; }
.node { fill: #ccc; /*stroke: #000;*/ stroke-width: 1.5px; cursor: pointer;}
.node.fixed { fill: #f00; cursor: pointer;}
text { font: 10px sans-serif; pointer-events: none; text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff; }
</style>
<body>
<script src="d3/d3.v3.js"></script>
<div id="topologyArea"></div>
<script>
var nodes = [//it's not necessary to give x and y values to nodes. One gets created for every empty object you insert here like this {}
{id: 1, x: 470, y: 410, icon: "images/abc.jpg"},
{id: 2, x: 493, y: 364, icon: "images/abc.jpg"},
{id: 3, x: 442, y: 365, icon: "images/abc.jpg"},
{id: 4, x: 467, y: 314, icon: "images/abc.jpg"},
{id: 5, x: 477, y: 248, icon: "images/fsd.jpg"},
{id: 6, x: 425, y: 207, icon: "images/sdfs.jpg"},
{id: 7, x: 402, y: 155, icon: "images/dfs.jpg"},
{id: 8, x: 369, y: 196, icon: "images/abc.jpg"},
{id: 9, x: 350, y: 148, icon: "images/abc.jpg"},
{id: 10, x: 539, y: 222, icon: "images/abc.jpg"},
{id: 11, x: 594, y: 235, icon: "images/abc.jpg"},
{id: 12, x: 582, y: 185, icon: "images/abc.jpg"},
{id: 13, x: 633, y: 200, icon: "images/abc.jpg"}
];
var links = [
{id: 1, source: 0, target: 1},
{id: 2, source: 1, target: 2},
{id: 3, source: 0, target: 2},
{id: 4, source: 1, target: 3},
{id: 5, source: 3, target: 2},
{id: 6, source: 3, target: 4},
{id: 7, source: 4, target: 5},
{id: 8, source: 5, target: 6},
{id: 9, source: 5, target: 7},
{id: 10, source: 6, target: 7},
{id: 11, source: 6, target: 8},
{id: 12, source: 7, target: 8},
{id: 13, source: 9, target: 4},
{id: 14, source: 9, target: 11},
{id: 15, source: 9, target: 10},
{id: 16, source: 10, target: 11},
{id: 17, source: 11, target: 12},
{id: 18, source: 12, target: 10}
];
var margin = {top: -5, right: -5, bottom: -5, left: -5}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom;
var iconOffset = -10, iconSize = 20;
var mousedown_node = null, mouseup_node = null, mousedown_link = null;
var nodeDeletionActivated = false;
var zoom = d3.behavior.zoom().scaleExtent([0.2, 2]).on("zoom", zoomed);
var svg = d3.select("#topologyArea").append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).attr('class', 'background').attr("transform", "translate(" + margin.left + "," + margin.right + ")");
var rect = svg.append("rect").attr("fill","transparent").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom)
.on("mousedown", mousedownOnBackground);
rect.call(zoom);
var elementHolderLayer = svg.append("g");;
var linkLayer, nodeLayer;
d3.select(window).on("keydown", keydown);// add keyboard callback
redraw(elementHolderLayer);
function redraw(theLayer)//after updating the nodes and links arrays, use this function to re-draw the force graph
{
var force = d3.layout.force().size([width, height]).charge(-400).linkDistance(40).on("tick", tick);
var dragElement = force.drag().on("dragstart", dragstarted);
linkLayer = null; nodeLayer = null;
linkLayer = theLayer.selectAll(".link");
nodeLayer = theLayer.selectAll(".node");
linkLayer = linkLayer.data(links, function(d) {return d.id; }).exit().remove();
linkLayer = theLayer.selectAll(".link").data(links, function(d) {return d.id; }).enter().append("line").attr("class", "link");
nodeLayer = nodeLayer.data(nodes, function(d) {return d.id; }).exit().remove();
nodeLayer = theLayer.selectAll(".node").data(nodes, function(d) {return d.id; }).enter().append("circle").attr("class", "node").attr("r", 12)
.on("dblclick", dblclick).style("fill", function(d,i) { return d3.rgb(i*15, i*15, i*15); })
.on("mouseup", function(d,i) { mouseup(d,i);})
.on("mousemove", function(d,i) {mousemove(d,i);})
.on("mousedown", function(d,i) {mousedown(d,i);})
.call(dragElement)
//.classed("dragging", true)
.classed("fixed", function(d) {d.fixed = true;});
force.nodes(nodes).links(links).start();
}//redraw
function tick()
{
linkLayer.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
nodeLayer.attr("cx", function(d) {return d.x; }).attr("cy", function(d) { return d.y; });
}
function dblclick(d) { d3.select(this).classed("fixed", d.fixed = false); }
function dragstarted(d)
{
console.log("dragstarted for "+this);
//d3.event.sourceEvent.stopPropagation();
//d3.select(this).classed("dragging", true);
//d3.select(this).classed("fixed", d.fixed = true);
}
function zoomed() { elementHolderLayer.attr("transform", "translate("+d3.event.translate+")scale(" + d3.event.scale + ")"); }
function spliceLinksForNode(node) //remove the links attached to a node that got deleted
{
toSplice = links.filter(function(l) { return (l.source === node) || (l.target === node); });
toSplice.map(function(l) {links.splice(links.indexOf(l), 1); });
}
function keydown()
{
//if (!selected_node && !selected_link) return;
switch (d3.event.keyCode)
{
case 8:
{// backspace
}
case 46:
{ // delete
if (mousedown_node)
{
selected_node = mousedown_node;
if (selected_node)
{
nodes.splice(nodes.indexOf(selected_node), 1);
spliceLinksForNode(selected_node);
}
else if (selected_link) { links.splice(links.indexOf(selected_link), 1); }
selected_link = null;
selected_node = null;
redraw(elementHolderLayer);
}
break;
}
}
}//keydown
function mousedown(d,i) { mousedown_node = d; console.log("mousedown"); }
function mousedownOnBackground() {resetMouseVars();}
function mousemove(d, i) {console.log("mousemove");}
function mouseup(d, i) {console.log("mouseup");}
function resetMouseVars()
{
mousedown_node = null;
mouseup_node = null;
mousedown_link = null;
}
</script>
There is one problem in the redraw function in your code.
linkLayer = linkLayer.data(links, function(d) {return d.id; })
.exit()
.remove();
Above line has no use in your code since you are assigning the same variable with links having the old data again.
linkLayer = theLayer.selectAll(".link").data(links, function(d) { return d.id; })
.enter()
.append("line")
.attr("class", "link");
Same happens for nodes. Change your code as shown below.
//Creating links
linkLayer = theLayer.selectAll(".link").data(links, function(d) {
return d.id;
});
linkLayer.enter().append("line").attr("class", "link");
linkLayer.exit().remove();
//Creating Nodes with image icons
var gNodes = nodeLayer.enter().append("g")
.attr("class", "node")
.on("dblclick", dblclick).style("fill", function(d, i) {
return d3.rgb(i * 15, i * 15, i * 15);
})
.on("mouseup", mouseup)
.on("mousemove", mousemove)
.on("mousedown", mousedown)
.call(dragElement)
.classed("fixed", function(d) {
d.fixed = true;
});
gNodes.append("circle")
.attr("r", 12);
gNodes.append("svg:image")
.attr("class", "circle")
.attr("xlink:href",function(d){ return d.icon })
.attr("x", "-8px")
.attr("y", "-8px")
.attr("width", "16px")
.attr("height", "16px");
nodeLayer.exit().remove();
For updating position of circles and images easily, I have grouped them using g elements. So you will need to update the position of g element using transform attribute instead of updating cx and cy attributes of circle. Now, tick function will look like this. Updated fiddle
function tick() {
linkLayer.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
nodeLayer.attr("transform", function(d) {return "translate("+d.x+","+d.y+")"; });
}

Updating D3 data doesn't remove old data from faux-3d example

I am working on extending the example found here. I have a data source that will pull in data for different periods of time. The data is as expected and draws well individually. I added a timer to iterate through the result set. The following code is in the original example. There are three calls points, flyers, arcLines (only flyers is shown below)
svg.append("g").attr("class","flyers")
.selectAll("path").data(links)
.enter().append("path")
.attr("class","flyer")
.attr("d", function(d) { return swoosh(flying_arc(d)) })
I changed the code to this to allow updates and they kind of work. The lines/points/flyers are removed and only the data from the time interval are displayed.
var flyer_data = svg_flyers
.selectAll("path").data(links);
flyer_data
.exit().remove();
flyer_data
.enter().append("path")
.attr("class", "flyer")
.attr("d", function(d) {
return swoosh(flying_arc(d))
});
Now the data isn't displayed correctly when the points and lines are supposedly in focus. Only data that is right on the horizon is displayed before its clipped. There is another side effect of the above code. The bound data structure is changed correctly but the SVG gets more and more points/lines/arcs which are not displayed. This last observation is particular baffling with the ever growing SVG definitions. It slows down the browser after a few minutes.
I have created a gist with enough code to demonstrate the issue. The Gist can be found here.
Your link to gist isn't working.
However, depending on your situation you can select your object (in this case your path) by class or id. In the example I've provided you can give your path an id and refer to it.
var lineData = [ { "x": 1, "y": 5}, { "x": 20, "y": 20},
{ "x": 40, "y": 10}, { "x": 60, "y": 40},
{ "x": 80, "y": 5}, { "x": 100, "y": 60}];
var lineData2 = [ { "x": 5, "y": 1}, { "x": 20, "y": 20},
{ "x": 10, "y": 40}, { "x": 40, "y": 60},
{ "x": 5, "y": 80}, { "x": 60, "y": 100}];
var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");
d3.select("svg")
.append("path")
.attr("id", "MyUniqueId")
.attr("d", lineFunction(lineData));
d3.select("#MyUniqueId")
.transition()
.duration(5000)
.attr("d", lineFunction(lineData2));
See js fiddle:
http://jsfiddle.net/3M54q/3/

Categories