I'm trying to use this sample code.
My question is for this line : d3.json("miserables.json", function(error, graph){}
Where should I put the miserables.json file?
My d3.js is in static/js/d3.py
Should I just put the file under static/js?
I have tried this, but all I got is a blank page.
I'm not familiar with how path works for d3.json()
My html file:
{{extend 'layout.html'}}
<div id="vi"></div>
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.select("#vi").json("miserables.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) { return color(d.group); })
.call(force.drag);
node.append("title")
.text(function(d) { return d.name; });
force.on("tick", function() {
link.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; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
</script>
<p>k<p>
This response.files.append(URL('static', 'plugin_d3/d3/d3.js')) is in my function code already. And there's an copy of the json code under static/plugin_d3/d3 also!
Please bear me for this simple question.
Thanks!!
Yes, you can put miserable.json in the /static folder (perhaps in a subfolder within /static). You then have to use the correct URL to refer to it. Given that you code is in a web2py template, you can use the URL() function to generate the URL:
d3.select("#vi").json("{{=URL('static', 'miserables.json')}}", ...)
In your original code:
d3.select("#vi").json("miserables.json", ...)
"miserable.json" is a relative URL, so the browser will just append that to the URL of the current page, which would end up being something like /yourapp/default/index/miserables.json. Instead, you need to specify the full URL, such as /yourapp/static/miserables.json (which the URL() function will generate).
Related
I am trying to make d3 force simulation where i am parsing data from csv file and i am trying use hover and showing text over node but i am unable to show it though i am able to parsing csv file.
Here is my code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link {
stroke: #000;
}
.node {
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.size([width, height]);
d3.csv("data.csv", function(error, links) {
if (error) throw error;
var nodesByName = {};
// Create nodes for each unique source and target.
links.forEach(function(link) {
link.source = nodeByName(link.source);
link.target = nodeByName(link.target);
});
// Extract the array of nodes from the map by name.
var nodes = d3.values(nodesByName);
// Create the link lines.
var link = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link");
// Create the node circles.
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 4.5)
.call(force.drag);
// Start the force layout.
force
.nodes(nodes)
.links(links)
.on("tick", tick)
.start();
function tick() {
link.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; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
function nodeByName(name) {
return nodesByName[name] || (nodesByName[name] = {name: name});
}
});
</script>
Here, Is my output
Actually, I am new in d3 force simulation, any suggestion is highly appreciated.
Thanks
Perhaps this would work,
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 4.5)
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut);
.call(force.drag);
and then call the functions as in the example here http://bl.ocks.org/WilliamQLiu/76ae20060e19bf42d774
I know it's there're a few topics that discuss the issue but non of the found results worked for me. I'm trying to modify the classic d3 network graph Les miserables example (d3v4 version HERE: https://bl.ocks.org/mbostock/4062045) to add different images for different nodes - the relative path of the file being given as one of the node attributes, eg.
{"id": "Valjean", "group": 1, img: "images/male.png"},
What I'm trying to achieve is similar to this:https://bl.ocks.org/mbostock/950642 but made in d3v4, and different images for different nodes.
All examples that I found (also this promissing code snippet, which unfortunately doesnt't work for me) point me to similar approach, which looks more or less like this(both in d3 v4 and v3):
node.append("image")
.attr("xlink:href", function(d) { return d.img })
.attr("x", -8)
.attr("y", -8)
.attr("width", 16)
.attr("height", 16);
However, despite a few hours spent I can not make it work. Any ideas?
Here's a quick example which combines your v4 example with an image based on your v3 example:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("https://jsonblob.com/api/9f7e2c48-8ccd-11e7-8b46-ef38640909a4", function(error, graph) {
if (error) throw error;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("image")
.data(graph.nodes)
.enter().append("image")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", -8)
.attr("y", -8)
.attr("width", 16)
.attr("height", 16)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) {
return d.id;
});
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
link
.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;
});
node
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script> d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
Adding in your images should then be as simple as changing and .attr("xlink:href" to:
.attr("xlink:href", function(d){
return d.img;
)};
we are using a D3 forced digraph from the following link:
http://bl.ocks.org/jose187/4733747
i was unable to figure out how to enable click on a node. did notice that we have the coordinates but not sure how to "attach" a click event handler.
any ideas would be most appreciated.
In D3, the listeners are attached using selection.on:
Adds or removes a listener to each selected element for the specified event typenames.
So, for a click event, it's simple as this:
node.on("click", function(){
//your code here
}
Check the demo with your code:
<script src="http://d3js.org/d3.v2.min.js?2.9.3"></script>
<style>
.link {
stroke: #aaa;
}
.node text {
stroke:#333;
cursor:pointer;
}
.node circle{
stroke:#fff;
stroke-width:3px;
fill:#555;
}
</style>
<body>
<script>
var width = 400,
height = 300
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.gravity(.05)
.distance(100)
.charge(-100)
.size([width, height]);
var json = {
"nodes":[
{"name":"node1","group":1},
{"name":"node2","group":2},
{"name":"node3","group":2},
{"name":"node4","group":3}
],
"links":[
{"source":2,"target":1,"weight":1},
{"source":0,"target":2,"weight":3}
]
};
force
.nodes(json.nodes)
.links(json.links)
.start();
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.weight); });
var node = svg.selectAll(".node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r","5");
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name });
node.on("click", function(d){
alert("hello, I'm " + d.name)
})
force.on("tick", function() {
link.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; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
</script>
I have used the chart example as given here:
http://bl.ocks.org/natemiller/20f9bd99d1795e3a0b1c
However, when trying to plot the data points over the individual lines, it doesnt show up. The code is given here:
var cities = svg.selectAll(".city")
.data(data, function(d) { return d.key; })
.enter().append("g")
.attr("class", "city");
cities.append("path") //adding paths
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.key); });
cities.selectAll(".dot") //adding dots
.data(data, function(d) { return d.key; })
.enter().append("circle")
.attr("class","dualLineChart-dot1")
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.values); })
.attr("r", 3.5)
.on("mouseover", function(d){
d3.select(this).style("fill", "blue");
})
.on("mouseout", function(){
d3.select(this).style("fill", "white");
});
the CSS part is as follows:
.line {
fill: none;
stroke-width: 1.5px;
}
.dot {
fill: white;
stroke: steelblue;
stroke-width: 1.5px;
}
You'll need to make a few changes to get this to work:
Make sure your class names are consistent: Change cities.selectAll(".dot") to cities.selectAll(".dualLineChart-dot1") to match with the class attr you assign a few lines later
The y-accessor is d.temp, rather than d.values, so you should have .attr("cy", function(d) { return y(d.temp); }) to get the y-value
Most importantly, you should change the way you get the data for the points. Because the cities variable already is an array of data (split up by city), you just need to access it for your points using .data(function(d) { return d.values; }), instead of using .data(data, function(d) { return d.values; })
Here's the working code:
cities.selectAll(".dualLineChart-dot1") //adding dots
.data(function(d) { return d.values; })
.enter().append("circle")
.attr("class","dualLineChart-dot1")
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.temp); })
.attr("r", 3.5)
.on("mouseover", function(d){
d3.select(this).style("fill", "blue");
})
.on("mouseout", function(){
d3.select(this).style("fill", "white");
});
Using and playing around with the Les Miserables Json data, I coded from modifications in d3. I was able to code everything except highlighting/marking a node if it is in the "fixed" state (vs the force graph layout). When you double click a node, it will freeze, a drag and move will pin it somewhere while all the other nodes (by default) in the force layout. When you double click the node again, the node is unpinned, and floating in the force layout as usual. The second thing I wanted to do is that if the node is in a “pinned” state, the node should be a different color, or highlighted somehow. I tried a few ways within the method chaining, checking with conditional statements for the node state to modify the node features, but they are not working. I also separated out the dblclick handler as an outside function (which is my current version.
I put my code here of the original attempt within the method chains:
http://pastebin.com/SqrqgVET
I also tried another way, using a variable “pinned” to determine the state of the node, within the double-click, to modify the nodes border, and change the state accordingly but that is not changing the node’s appearance either. There is a console.log() statement, and the function is going into the if else portions accordingly.
http://pastebin.com/dzEw42mQ
Below is the current version.
Any feedback would be great. Thanks!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
cursor: move;
stroke: #fff;
stroke-width: 1.5px;
}
.node.fixed {
fill: #f00;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
</style>
<head>
<title>Victor Hugo Had No Internet</title>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="http://d3js.org/colorbrewer.v1.min.js"></script>
</head>
<body>
<script type="text/javascript">
//Size of region to render on
var width = 960,
height = 500;
var color = d3.scale.ordinal()
.domain([1, 10])
.range(colorbrewer.BrBG[9]);
//D3 force directed layout
//Try playing with the charge and link distance
var force = d3.layout.force()
.charge(-100)
.linkDistance(40)
//.on("tick", tick) //event ADDED
.size([width, height]);
//Add our canvas
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//Select the miserables data ;)
d3.json("miserables.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
//Add the links
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
//Add the nodes
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", function(d){ return Math.sqrt(d.coolness);})
.attr("stroke", "#ffffff")
.on("dblclick", dblclick)
.call(force.drag)
.style("fill", function(d) {return color(d.group);}); //});
//.style("border", 5);//function(d) {
//if (d.fixed==false) {return 4}; });
// node.append("text") //label
// .attr("dx", 6)
// .attr("dy", ".10em")
// .text(function(d) { return d.name; });
//add labels
var labels = svg.selectAll("text")
.data(graph.nodes)
.enter()
.append("text")
.attr({"x":function(d){return d.x;},
"y":function(d){return d.y;}})
.text(function(d) {
if (d.coolness>25 && d.name.length > 6) {return d.name.substring(0,6)+'...'}
if (d.coolness>25 && d.name.length < 6) {return d.name}
else { return null } ;})
.call(force.drag);
//Update stuff for animation:
// This takes the physics simulation for the force directed graph and
// sets the location of the nodes and edges to the new positions
force.on("tick", function() {
link.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; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
labels.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
});
// action to take on mouse double click
function dblclick(d) { //color, stroke not working.
//var pinned = d3.select(this).attr("stroke");
console.log("dblclick")
if (d.fixed == true) { //pinned state
console.log("pinned")
d3.select(this)
.attr("stroke", "#ffffff")
.attr("stroke-width", 1.5)
.classed("fixed", d.fixed = false);//now unpin
} else { //else not pinned state
console.log("not pinned")
d3.select(this)
.attr("stroke", "#000000")
.attr("stroke-width", 4)
.classed("fixed", d.fixed = true);
}
}//end dbl click
});
</script>`
Here is the output of the console log (where I added console.log(this) to check the right element is being passed to the dblclick function and if parameters are being set accordingly:
[Log] dblclick (miserables_graph.html, line 131)
[Log] <circle class="node fixed" r="9.1104335791443" stroke="#000000" style="fill: #f6e8c3;" cx="630.38114584665" cy="98.39845698676822" stroke-width="4"></circle> (miserables_graph.html, line 132)
[Log] not pinned (miserables_graph.html, line 140)
[Log] dblclick (miserables_graph.html, line 131)
[Log] <circle class="node" r="9.1104335791443" stroke="#ffffff" style="fill: #f6e8c3;" cx="630.38114584665" cy="98.39845698676822" stroke-width="1.5"></circle> (miserables_graph.html, line 132)
[Log] pinned (miserables_graph.html, line 134)