How to align two svg's side by side in d3.js - d3.js

I have two graphs(rectangles with text the other circles with text) to be displayed but instead of displaying one below the other,I want to display side by side i.e 2 charts displaying horizontally(one at the left of div and the other at right of div). I created 2 svg's and added the charts in to that. However when ever i change the top or bottom margin it is not working.
My code goes like:
<!DOCTYPE html>
<html xmlns:xlink="http://www.w3.org/1999/xlink">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Circle legends</title>
<script type="text/javascript" src="../../js/d3/d3.v3.js"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?1.27.2"></script>
<link rel="stylesheet" href="../../css/style.css" />
<style type="text/css">
</style>
</head>
<body>
<div id="chart"></div>
<script type="text/javascript">
var width=960,height=500;
var margin = {top: 29.5, right: 29.5, bottom: 29.5, left: 59.5};
radiusScale = d3.scale.sqrt().domain([1, 100]).range([10, 39]),
colorScale = d3.scale.category10();
//Create the SVG for legends.
var svglegend = d3.select("#chart").append("svg").attr("id","svglegend")
.attr("width", width-100)
.attr("height", height -100)
.append("g")
.attr("transform", "translate(" + margin.left + "," + (margin.top +30) + ")");
//alert("Non-empty");
d3.json("SbuLegendData.json", function(data) {
jsondata = data;
rectangle= svglegend.selectAll("rect").data(data).enter().append("rect");
var RectangleAttrb = rectangle.attr("x", function (d) { return d.x_axis; })
.attr("y", function (d) { return d.y_axis; })
.attr("width",function(d) { return d.width; } )
.attr("height",function(d) { return d.height; })
.style("fill", function(d) { return d.color; });
var textparam = svglegend.selectAll("text").data(data).enter().append("text");
var text = textparam .attr("x", function (d) { return d.x_axis + d.width +10; })
.attr("y", function (d) { return d.y_axis + d.height-5; })
.attr("width",30 )
.attr("height",20)
.text(function(d) { return d.text; });
});
// Create the SVG container and set the origin.
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var i =0;
while (i<=50)
{
console.log("i value is " + i + " value of scale " +i+ " is " + radiusScale(i));
var g = svg.append("g");
g.append("circle")
.attr("id","circle" + i)
.attr("cx", i*12 )
.attr("cy", 30)
.attr("fill",colorScale(i))
.attr("r", radiusScale(i));
g.append("text").attr("dx",i*12).text(Math.round(radiusScale(i)));
i=i+10;
}
</script>
</body>
</html>
the json data contains:
[
{ "x_axis":40, "y_axis": 10,"width":50,"height":20,"color" : "#1f77b4","text":"F&R"},
{ "x_axis":40, "y_axis": 30,"width":50,"height":20,"color" : "#ff7f0e","text":"Legal"},
{ "x_axis":40, "y_axis": 50,"width":50,"height":20,"color" : "#2ca02c","text":"GGO"},
{ "x_axis":40, "y_axis": 70,"width":50,"height":20,"color" : "#d62728","text":"IP&S"},
{ "x_axis":40, "y_axis": 90,"width":50,"height":20,"color" : "#9467bd","text":"CORP"},
{ "x_axis":40, "y_axis": 110,"width":50,"height":20,"color": "#8c564b","text":"TAX"},
{ "x_axis":40, "y_axis": 130,"width":50,"height":20,"color" : "#e377c2","text":"REUTERS ALL"}
]

You need to use CSS for this. It's easier if you have two containers:
CSS:
.container {
float: left;
}
HTML:
<div class="container" id="chart1">
</div>
<div class="container" id="chart2">
</div>
Then use #chart1 and #chart2 in your code.

Related

How to show images on doughnut chart slice?

I have a project where I need to display a doughnut chart. For every slice in the chart there is a corresponding icon in the legend. This icon should also been shown on the slice itself inside the chart.
I have found a working example online on how to display images on doughnut charts: Working example. I have tried to implement this solution into my own project. The images get loaded in and when I inspect the SVG each path node(slice) contains an image element with the correct image. But the images don't show up on the graph.
This is the code i am running atm. If you have some pointers on how to improve my overall code then you're welcome to do so. I am still new to D3.JS and learning a lot about it at the moment:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://unpkg.com/vue"></script>
<script src="https://d3js.org/d3.v6.js"></script>
</head>
<body>
<div class="p-3 flex flex-col" id="one">
<div class="w-full flex-1">
<div id="my_dataviz"></div>
</div>
</div>
<script>
new Vue({
el: '#one',
data: {
type: Array,
required: true,
}, mounted() {
// set the dimensions and margins of the graph
var width = 450;
var height = 450;
var margin = 1;
var image_width = 32;
var image_height = 32;
var data = [
{
key: "One",
value: 20,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
},
{
key: "Two",
value: 30,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
},
{
key: "Three",
value: 10,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
},
{
key: "Four",
value: 15,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
}
]
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
// append the svg object to the div called 'my_dataviz'
var svg = d3
.select('#my_dataviz')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr(
'transform',
'translate(' + width / 2 + ',' + height / 2 + ')'
);
var radius = Math.min(width, height) / 2 - margin;
// set the color scale
var color = d3
.scaleOrdinal()
.domain(
data.map(function(d) {
return d["key"];
})
)
.range(["#206BF3"]);
// Compute the position of each group on the pie:
var pie = d3.pie().value(function(d) {
return d[1];
});
var data_ready = pie(
data.map(function(d) {
return [d["key"], d["value"], d["icon"]];
})
);
// declare an arc generator function
var arc = d3
.arc()
.outerRadius(100)
.innerRadius(50);
console.log(arc);
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
var paths = svg
.selectAll("whatever")
.data(data_ready)
.enter()
.append("path")
.attr("d", d => {
return arc(d);
})
.attr("fill", function(d) {
return color(d.data[0]);
})
.attr("stroke", "#2D3546")
.style("stroke-width", "2px")
.style("opacity", 0.7);
paths
.append("svg:image")
.attr("transform", function(d) {
var x = arc.centroid(d)[0] - image_width / 2;
var y = arc.centroid(d)[1] - image_height / 2;
return "translate(" + width / 2 + x + "," + height + y + ")";
})
.attr("xlink:href", function(d) {
console.log(d);
return d.data[2];
})
.attr("width", image_width)
.attr("height", image_height);
paths.on("mouseover", e => {
this.pathAnim(radius, d3.select(e.currentTarget), 1);
});
paths.on("mouseout", e => {
var thisPath = d3.select(e.currentTarget);
if (!thisPath.classed("clicked")) {
this.pathAnim(radius, thisPath, 0);
}
});
},
methods: {
pathAnim(radius, path, dir) {
switch (dir) {
case 0:
path
.transition()
.duration(500)
.ease(d3.easeBounce)
.attr(
"d",
d3
.arc()
.innerRadius(100)
.outerRadius(50)
);
path.style("fill", "#206BF3");
break;
case 1:
path.transition().attr(
"d",
d3
.arc()
.innerRadius(50)
.outerRadius(110)
);
path.style("fill", "white");
break;
}
}
}
});
</script>
</body>
</html>
A <path> element cannot contain an <image>. Instead of that, use the data to create <g> elements and append both the <path> and the <image> to them:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://unpkg.com/vue"></script>
<script src="https://d3js.org/d3.v6.js"></script>
</head>
<body>
<div class="p-3 flex flex-col" id="one">
<div class="w-full flex-1">
<div id="my_dataviz"></div>
</div>
</div>
<script>
new Vue({
el: '#one',
data: {
type: Array,
required: true,
},
mounted() {
// set the dimensions and margins of the graph
var width = 450;
var height = 450;
var margin = 1;
var image_width = 32;
var image_height = 32;
var data = [{
key: "One",
value: 20,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
},
{
key: "Two",
value: 30,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
},
{
key: "Three",
value: 10,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
},
{
key: "Four",
value: 15,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
}
]
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
// append the svg object to the div called 'my_dataviz'
var svg = d3
.select('#my_dataviz')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr(
'transform',
'translate(' + width / 2 + ',' + height / 2 + ')'
);
var radius = Math.min(width, height) / 2 - margin;
// set the color scale
var color = d3
.scaleOrdinal()
.domain(
data.map(function(d) {
return d["key"];
})
)
.range(["#206BF3"]);
// Compute the position of each group on the pie:
var pie = d3.pie().value(function(d) {
return d[1];
});
var data_ready = pie(
data.map(function(d) {
return [d["key"], d["value"], d["icon"]];
})
);
// declare an arc generator function
var arc = d3
.arc()
.outerRadius(100)
.innerRadius(50);
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
var g = svg
.selectAll("whatever")
.data(data_ready)
.enter()
.append("g")
.attr("transform", function(d) {
var x = arc.centroid(d)[0] - image_width / 2;
var y = arc.centroid(d)[1] - image_height / 2;
return "translate(" + width / 2 + x + "," + height + y + ")";
});
g.append("path")
.attr("d", d => {
return arc(d);
})
.attr("fill", function(d) {
return color(d.data[0]);
})
.attr("stroke", "#2D3546")
.style("stroke-width", "2px")
.style("opacity", 0.7);
g.append("svg:image")
.attr("transform", function(d) {
var x = arc.centroid(d)[0] - image_width / 2;
var y = arc.centroid(d)[1] - image_height / 2;
return "translate(" + x + "," + y + ")";
})
.attr("xlink:href", function(d) {
return d.data[2];
})
.attr("width", image_width)
.attr("height", image_height);
g.on("mouseover", e => {
this.pathAnim(radius, d3.select(e.currentTarget), 1);
});
g.on("mouseout", e => {
var thisPath = d3.select(e.currentTarget);
if (!thisPath.classed("clicked")) {
this.pathAnim(radius, thisPath, 0);
}
});
},
methods: {
pathAnim(radius, path, dir) {
switch (dir) {
case 0:
path
.transition()
.duration(500)
.ease(d3.easeBounce)
.attr(
"d",
d3
.arc()
.innerRadius(100)
.outerRadius(50)
);
path.style("fill", "#206BF3");
break;
case 1:
path.transition().attr(
"d",
d3
.arc()
.innerRadius(50)
.outerRadius(110)
);
path.style("fill", "white");
break;
}
}
}
});
</script>
</body>
</html>
I've updated #GerardoFurtado's code. I just moved the events to the paths and added pointer-events: none for images. Transitions work well.
g image {
pointer-events: none;
}
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://unpkg.com/vue"></script>
<script src="https://d3js.org/d3.v6.js"></script>
</head>
<body>
<div class="p-3 flex flex-col" id="one">
<div class="w-full flex-1">
<div id="my_dataviz"></div>
</div>
</div>
<script>
new Vue({
el: '#one',
data: {
type: Array,
required: true,
},
mounted() {
// set the dimensions and margins of the graph
var width = 450;
var height = 450;
var margin = 1;
var image_width = 32;
var image_height = 32;
var data = [{
key: "One",
value: 20,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
},
{
key: "Two",
value: 30,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
},
{
key: "Three",
value: 10,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
},
{
key: "Four",
value: 15,
icon: "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"
}
]
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
// append the svg object to the div called 'my_dataviz'
var svg = d3
.select('#my_dataviz')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr(
'transform',
'translate(' + width / 2 + ',' + height / 2 + ')'
);
var radius = Math.min(width, height) / 2 - margin;
// set the color scale
var color = d3
.scaleOrdinal()
.domain(
data.map(function(d) {
return d["key"];
})
)
.range(["#206BF3"]);
// Compute the position of each group on the pie:
var pie = d3.pie().value(function(d) {
return d[1];
});
var data_ready = pie(
data.map(function(d) {
return [d["key"], d["value"], d["icon"]];
})
);
// declare an arc generator function
var arc = d3
.arc()
.outerRadius(100)
.innerRadius(50);
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
var g = svg
.selectAll("whatever")
.data(data_ready)
.enter()
.append("g")
/* I commented this lines and nothing changed.
.attr("transform", function(d) {
var x = arc.centroid(d)[0] - image_width / 2;
var y = arc.centroid(d)[1] - image_height / 2;
return "translate(" + width / 2 + x + "," + height + y + ")";
});
*/
g.append("path")
.attr("d", d => {
return arc(d);
})
.attr("fill", function(d) {
return color(d.data[0]);
})
.attr("stroke", "#2D3546")
.style("stroke-width", "2px")
.style("opacity", 0.7)
.on("mouseover", e => {
console.log(this)
this.pathAnim(radius, d3.select(e.currentTarget), 1);
})
.on("mouseout", e => {
var thisPath = d3.select(e.currentTarget);
if (!thisPath.classed("clicked")) {
this.pathAnim(radius, thisPath, 0);
}
});
g.append("svg:image")
.attr("transform", function(d) {
var x = arc.centroid(d)[0] - image_width / 2;
var y = arc.centroid(d)[1] - image_height / 2;
return "translate(" + x + "," + y + ")";
})
.attr("xlink:href", function(d) {
return d.data[2];
})
.attr("width", image_width)
.attr("height", image_height);
},
methods: {
pathAnim(radius, path, dir) {
switch (dir) {
case 0:
path
.transition()
.duration(500)
.ease(d3.easeBounce)
.attr(
"d",
d3
.arc()
.innerRadius(100)
.outerRadius(50)
);
path.style("fill", "#206BF3");
break;
case 1:
path.transition().attr(
"d",
d3
.arc()
.innerRadius(50)
.outerRadius(110)
);
path.style("fill", "white");
break;
}
}
}
});
</script>
</body>
</html>

D3.JS bar graph in Firefox cshtml

I am trying to put together an interactive bar chart in cshtml.
The good news is it works on every browser except for Firefox.
That being said I'd very much like to know why it is failing on Firefox when it even works on Internet Explorer.. I mean come on, the internet doesn't even work on Internet Explorer.
I have added in what I believe to be the relevant patch of code here:
function buildVisualization(dataSet) {
var barWidth = (chartWidth / dataSet.Items.length - 1) - 1;
var bars = svg.selectAll("rect")
.data(dataSet.Items);
// Build bars for each item
// Example "rect" element: <rect x="200" y="400" width="300" height="100" style="" class="" />
bars.enter()
.append("rect")
.attr("x", function (item, i) { return xScale(new Date(item.DateAsked)) } )
.attr("y", function (item, i) { return chartHeight - yScale(item.Rate)})
.attr("width", function (item) { return barWidth})
.attr("height", function (item) { return yScale(item.Rate)})
.attr("fill", "teal");
bars.exit().remove();
bars.transition()
.attr("x", function (item, i) { return xScale(new Date(item.DateAsked))} )
.attr("y", function (item, i) { return chartHeight - yScale(item.Rate)})
.attr("width", function (item) { return barWidth})
.attr("height", function (item) { return yScale(item.Rate)})
.attr("fill", "teal");
}
That being said I can provide any information required if requested.
I should point out that when run the chart itself is put into the right place however the bars (an important bit of a bar chart) are all pushed off to the left and stacked on top of each other though they do change height when different options are selected so it seems to be something wrong with the positioning rather than with how they are created. Any advice would be quite welcome.
Entire Snippet:
#{
ViewBag.Title = "Bar Chart";
var choices = new List<SelectListItem>
(){
new SelectListItem(){Text= "C#", Value="c#", Selected=true },
new SelectListItem(){Text= ".Net", Value=".net" },
new SelectListItem(){Text= "ASP.Net", Value="asp.net" },
new SelectListItem(){Text= "ASP.Net MVC", Value="asp.net-mvc" },
new SelectListItem(){Text= "C", Value="c" },
new SelectListItem(){Text= "C++", Value="c++" },
new SelectListItem(){Text= "JavaScript", Value="javascript" },
new SelectListItem(){Text= "Objective C", Value="objective-c" },
new SelectListItem(){Text= "PHP", Value="php" },
new SelectListItem(){Text= "Ruby", Value="ruby" },
new SelectListItem(){Text= "Python", Value="python" }
};
}
<style type="text/css">
svg g.axis {
font-size: .75em;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
svg g.axis text.label {
font-size: 2em;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
svg g.axis path,
svg g.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
</style>
<h2>#ViewBag.Title</h2>
<div class="row">
<div class="col-md-8">
<p>This demo takes tag information from data.stackexchange.com and projects it below.</p>
</div>
</div>
<div class="row">
<div class="col-md-4">
#Html.Label("TagChoice", "Tag")
#Html.DropDownList("TagChoice", choices)
</div>
</div>
<div class="row">
<div id="chartContainer">
</div>
</div>
#Scripts.Render("~/bundles/d3")
#section Scripts{
<script type="text/javascript">
$(document).ready(function () {
$("#TagChoice").on("change", function () {
var tag = $(this).val();
var url = "/api/tags?tag=";
url += encodeURIComponent(tag);
$.getJSON(url, function (data) {
buildVisualization(data);
});
});
$("#TagChoice").change();
});
// Overall dimensions of the SVG
var height = 400;
var width = 900;
// Padding...
var leftPadding = 75;
var bottomPadding = 50;
// Actual space for the bars
var chartWidth = width - leftPadding;
var chartHeight = height - bottomPadding;
//Building the scale for the heights
var yScale = d3.scale
.linear()
.range([0, chartHeight])
.domain([0, 21000]);
var yAxisScale = d3.scale
.linear()
.range([chartHeight, 0])
.domain([0, 21000]);
//Building the scale for the bar locations
var xScale = d3.time.scale()
.domain([new Date("5-1-2008"), new Date("2-1-2014")])
.range([leftPadding, width - 10]);
//Building a Y axis
var yAxis = d3.svg.axis()
.scale(yAxisScale)
.orient("left");
// Building an X Axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickFormat(d3.time.format("%m/%d/%Y"));
// Build the overall SVG container
var svg = d3.select("#chartContainer")
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("class", "chart");
// Adding the Axes
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + leftPadding + ",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("dy", "-55px")
.attr("dx", "-50px")
.attr("class", "label")
.style("text-anchor", "end")
.text("Number of Questions Asked");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + chartHeight + ")")
.call(xAxis)
.append("text")
.attr("dy", "40px")
.attr("dx", "475px")
.attr("class", "label")
.style("text-anchor", "end")
.text("Month Asked");
function buildVisualization(dataSet) {
var barWidth = (chartWidth / dataSet.Items.length - 1) - 1;
var bars = svg.selectAll("rect")
.data(dataSet.Items);
// Build bars for each item
// Example "rect" element: <rect x="200" y="400" width="300" height="100" style="" class="" />
bars.enter()
.append("rect")
.attr("x", function (item, i) { return xScale(new Date(item.DateAsked)) })
.attr("y", function (item, i) { return chartHeight - yScale(item.Rate) })
.attr("width", function (item) { return barWidth })
.attr("height", function (item) { return yScale(item.Rate) })
.attr("fill", "teal");
bars.exit().remove();
bars.transition()
.attr("x", function (item, i) { return xScale(new Date(item.DateAsked)) })
.attr("y", function (item, i) { return chartHeight - yScale(item.Rate) })
.attr("width", function (item) { return barWidth })
.attr("height", function (item) { return yScale(item.Rate) })
.attr("fill", "teal");
}
</script>
}
Perhaps the reason of your problem is that in Chrome
>> new Date("5-1-2008")
Thu May 01 2008 ...
while in Firefox:
>> new Date("5-1-2008")
Invalid Date
(this is relevant to lines, where you construct xScale)

How to link multiple graph networks in d3js so that an event in one calls the same event in other networks

I am drawing four network graphs of the same data but using different properties to color the node. Currently, I am able to generate all 4 networks and for each network I am able to double click a node to see the connected nodes of the network. My question is how do I extend this functionality so that if I click a node on any of the graph networks, the other 3 also show me the node I clicked and the connected nodes therein.
This question asked previously Mouseover event on two charts at the same time d3.js does a similar thing for pie charts but I am not sure how I can adapt it to my code.
var drawNetworkFunction = function(descriptor, chartId) {
var width = 470;
var height = 310;
var toggle = 0;
var activenode;
var svg = d3.select(chartId).append("svg")
.attr("width", width)
.attr("height", height);
var container = svg.append("g")
.attr("class", "everything");
if (descriptor == "id" || descriptor == "cluster") {
var color = d3.scaleOrdinal(d3.schemeCategory10);
}
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody().strength(-7))
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("Perovskite_info.json", function(graph) {
if (descriptor == "cohesive") {
var cscale = d3.scaleLinear()
.domain([1.6, 7.2])
.range([0, 1]);
} else if (descriptor == "similarity") {
var cscale = d3.scaleLinear()
.domain([0, 1])
.range([0, 1]);
}
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke", "#000")
.attr("stroke-opacity", "0.1");
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.attr("pvs_id", function(d) { return d.id; })
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) {
var col = '';
if (descriptor == "id") {
col = color(d.group);
} else if (descriptor == "cluster") {
col = color(d.cluster);
} else if (descriptor == "cohesive") {
col = d3.interpolatePurples(cscale(d.cohesive));
} else if (descriptor == "similarity") {
col = d3.interpolateReds(cscale(d.similarity));
}
return col;
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("dblclick", connectedNodes);
node.append("title")
.text(function(d) {
return ["Perovskite :" + d.id,
"Cluster :" + d.cluster,
"Cohesive Energy :" + d.cohesive,
"Similarity to CaTiO3 :" + d.similarity
];
});
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("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
var linkedByIndex = {};
for (i = 0; i < graph.nodes.length; i++) {
linkedByIndex[i + "," + i] = 1;
};
graph.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function neighboring(a, b) {
return linkedByIndex[a.index + "," + b.index];
}
function HS_f(activenode) {
if (activenode != null) {
var fing_plot = d3.select("#HSA_fingerprint")
.append("svg")
.attr("id", "fin_pl")
.attr("width", 300)
.attr("height", 300);
fing_plot.append('svg:image')
.attr("xlink:href", function() { return "./finger_prints_lo/" + activenode + "_lo.png" })
.attr("width", 250)
.attr("height", 250);
return fing_plot;
}
}
function connectedNodes() {
if (toggle == 0) {
d = d3.select(this).node().__data__;
node.style("opacity", function(o) {
return neighboring(d, o) | neighboring(o, d) ? 1 : 0.15;
});
toggle = 1;
activenode = d.id;
window.activenode = activenode;
console.log(activenode);
HS_f(activenode);
} else {
node.style("opacity", 1);;
toggle = 0;
d3.select("#fin_pl").remove();
}
}
});
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;
}
return svg;
}
For the json file please refer to the link: https://api.myjson.com/bins/m3gd8
Also my html file is below:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type=" text/javascript " src="http://code.jquery.com/jquery-latest.js "></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-color.v1.min.js"></script>
<script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/d3-dispatch.v1.min.js"></script>
<script src="draw_net.js"></script>
<title>Library of Perovskite structures from Materials Project Database</title>
<div id="title_main">
<h2>Graph Network of Perovskite structures from Materials Project Database</h2>
</div>
</head>
<body>
<link rel="stylesheet" href="pvs-net.css" />
<div style="width: 80%; float:left; overflow: auto;">
<div id="Perovskite_Network">
<div style="width: 50%; float:left;">
<div id="Network1">
<div id="title_1">
<h3>Isomap Graph Network</h3>
<script>
var Net1 = drawNetworkFunction('id', '#Network1');
</script>
</div>
</div>
<div id="Network2">
<div style="width: 50%; float:left; overflow: auto;"></div>
<div id="title_2">
<h3>Spectral Clustering on Isomap Graph Network</h3>
<script>
var Net2 = drawNetworkFunction('cluster', '#Network2');
</script>
</div>
</div>
</div>
<div style="width: 50%; float:right; overflow: auto;">
<div id="Network3">
<div id="title_3">
<h3>Calculated Cohesive Energy</h3>
<script>
var Net3 = drawNetworkFunction('cohesive', '#Network3');
</script>
</div>
</div>
<div id="Network4">
<div id="title_4">
<h3>Similarity score against CaTiO3</h3>
<script>
var Net4 = drawNetworkFunction('similarity', '#Network4');
</script>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="width: 20%; height: 500px; float:right;">
<div id="HSA_fingerprint">
<div id="title">
<h3>Hirschfeld Surface Fingerprint Plot</h3>
</div>
</div>
</div>
</body>
</html>
Glad you came back with the data and the HTML. It makes it easier to debug and come up with a solution.
I just changed a couple of lines of code in the connectedNodes function:
Instead of node.style("opacity", function(o) {...}), I replaced it with:
d3.select('#Perovskite_Network').selectAll('.nodes').selectAll('circle').style("opacity", function(o) {
return neighboring(d, o) | neighboring(o, d) ? 1 : 0.15;
});
This would select all circles from all the the SVGs. Let me know if that doesn't help or if you need any improvements.
Snippet:
var drawNetworkFunction = function(descriptor, chartId) {
var width = 470;
var height = 310;
var toggle = 0;
var activenode;
var svg = d3.select(chartId).append("svg")
.attr("width", width)
.attr("height", height);
var container = svg.append("g")
.attr("class", "everything");
if (descriptor == "id" || descriptor == "cluster") {
var color = d3.scaleOrdinal(d3.schemeCategory10);
}
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody().strength(-7))
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("https://api.myjson.com/bins/m3gd8", function(graph) {
if (descriptor == "cohesive") {
var cscale = d3.scaleLinear()
.domain([1.6, 7.2])
.range([0, 1]);
} else if (descriptor == "similarity") {
var cscale = d3.scaleLinear()
.domain([0, 1])
.range([0, 1]);
}
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke", "#000")
.attr("stroke-opacity", "0.1");
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.attr("pvs_id", function(d) { return d.id; })
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) {
var col = '';
if (descriptor == "id") {
col = color(d.group);
} else if (descriptor == "cluster") {
col = color(d.cluster);
} else if (descriptor == "cohesive") {
col = d3.interpolatePurples(cscale(d.cohesive));
} else if (descriptor == "similarity") {
col = d3.interpolateReds(cscale(d.similarity));
}
return col;
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("dblclick", connectedNodes);
node.append("title")
.text(function(d) {
return ["Perovskite :" + d.id,
"Cluster :" + d.cluster,
"Cohesive Energy :" + d.cohesive,
"Similarity to CaTiO3 :" + d.similarity
];
});
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("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
var linkedByIndex = {};
for (i = 0; i < graph.nodes.length; i++) {
linkedByIndex[i + "," + i] = 1;
}
graph.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function neighboring(a, b) {
return linkedByIndex[a.index + "," + b.index];
}
function HS_f(activenode) {
if (activenode != null) {
var fing_plot = d3.select("#HSA_fingerprint")
.append("svg")
.attr("id", "fin_pl")
.attr("width", 300)
.attr("height", 300);
fing_plot.append('svg:image')
.attr("xlink:href", function() { return "./finger_prints_lo/" + activenode + "_lo.png" })
.attr("width", 250)
.attr("height", 250);
return fing_plot;
}
}
function connectedNodes() {
if (toggle == 0) {
d = d3.select(this).node().__data__;
d3.select('#Perovskite_Network').selectAll('.nodes').selectAll('circle').style("opacity", function(o) {
return neighboring(d, o) | neighboring(o, d) ? 1 : 0.15;
});
toggle = 1;
activenode = d.id;
window.activenode = activenode;
//console.log(activenode);
HS_f(activenode);
} else {
d3.select('#Perovskite_Network').selectAll('.nodes').selectAll('circle').style("opacity", 1);
toggle = 0;
d3.select("#fin_pl").remove();
}
}
});
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;
}
return svg;
}
drawNetworkFunction('id', '#Network1');
drawNetworkFunction('cluster', '#Network2');
drawNetworkFunction('cohesive', '#Network3');
drawNetworkFunction('similarity', '#Network4');
<meta charset="utf-8">
<script type=" text/javascript " src="http://code.jquery.com/jquery-latest.js "></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-color.v1.min.js"></script>
<script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/d3-dispatch.v1.min.js"></script>
<script src="draw_net.js"></script>
<title>Library of Perovskite structures from Materials Project Database</title>
<div id="title_main">
<h2>Graph Network of Perovskite structures from Materials Project Database</h2>
</div>
<link rel="stylesheet" href="pvs-net.css" />
<div style="width: 80%; float:left; overflow: auto;">
<div id="Perovskite_Network">
<div style="width: 50%; float:left;">
<div id="Network1">
<div id="title_1">
<h3>Isomap Graph Network</h3>
</div>
</div>
<div id="Network2">
<div style="width: 50%; float:left; overflow: auto;"></div>
<div id="title_2">
<h3>Spectral Clustering on Isomap Graph Network</h3>
</div>
</div>
</div>
<div style="width: 50%; float:right; overflow: auto;">
<div id="Network3">
<div id="title_3">
<h3>Calculated Cohesive Energy</h3>
</div>
</div>
<div id="Network4">
<div id="title_4">
<h3>Similarity score against CaTiO3</h3>
</div>
</div>
</div>
</div>
</div>
<div style="width: 20%; height: 500px; float:right;">
<div id="HSA_fingerprint">
<div id="title">
<h3>Hirschfeld Surface Fingerprint Plot</h3>
</div>
</div>
</div>
And a fiddle link: https://jsfiddle.net/shashank2104/bj76ckh4/4/
I have a few suggestions though:
The file is being fetched for every drawNetworkFunction call which slows down the rendering of the page.
Along with fetching of the file, the object linkedByIndex is created in every call which is independent of the descriptor or chartId.
Suggestion: Move the above steps in a single function and pass the linkedByIndex and graph (data) to the drawNetworkFunction and then the rest. Hope this helps.

Responsive line chart with transitions using d3

I am trying to animate the attached code without success. I want the axis and path smoothly to shift in between the addition of new data points. The transition time should be deltaT. I hope someone could help me! (I got it working but only as long as the graph was not responsive, see attached js snippet)
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Transfer setup rpi1 interface</title>
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
<script src="jquery.flot.min.js"></script>
<script src="jquery.flot.time.js"></script>
<script src="//d3js.org/d3.v3.js" charset="utf-8"></script>
<script type="text/javascript" src="main.js"></script>
</head>
<body>
<div class="content">
<header>
<h1>Transfer Setup Rpi1</h1>
</header>
<div class="main">
<h2>Control</h2>
<fieldset>
<legend>Temperature</legend>
<div id="widthReader">
<table id="cssTable">
<tr>
<td>
<table id="cssTable">
<td><label for="toggleFan">Fan</label></td>
<td>
<input id="toggleFan" class="cmn-toggle cmn-toggle-round" type="checkbox">
<label for="toggleFan"></label>
</td>
</table>
</td>
<td>
</td>
<td>
<label>Speed:</label> <input type="text"
id="fanSpeed" name="fanSpeed" value="">
</td>
</tr>
<tr>
<td>
<table id="cssTable">
<td><label for="toggleHeater">Heater</label></td>
<td>
<input id="toggleHeater" class="cmn-toggle cmn-toggle-round" type="checkbox">
<label for="toggleHeater"></label>
</td>
</table>
</td>
<td>
</td>
<td>
<label>Setpoint:</label> <input type="text"
id="heaterTemp" name="heaterTemp" value="">
</td>
</tr>
</table>
</div>
</fieldset>
<button id="buttonSave">Save Settings</button>
<h2>Dashboard</h2>
<fieldset>
<legend>Chart</legend>
<label>Current Temperature:</label> <label id="heater_temperature"> </label><label>°C</label>
<div id="chart"></div>
</fieldset>
</div>
</div>
</body>
</html>
main.js:
var deltaT = 2500; //temperature update intervall
var Chart = (function(window,d3) {
var svg, data, x, y, xAxis, yAxis, dim, chartWrapper, line, path, margin = {}, width, height, locator, textsize, xlabeloff, ylabeloff, xtickpadding, ytickpadding, xtickdistance, ytickdistance;
var breakPoint = 400; //for rendering smaller fonts on mobile devices wiht small screen widths
var n = 10,//(4)*60,//length of recording
duration = deltaT,//duration of deltat in msec
now = new Date(Date.now() - duration),
count = 0,
data = Array.apply(null, new Array(n)).map(Number.prototype.valueOf,20);
//called once the data is loaded
function init() {
//initialize scales
x = d3.time.scale()
.domain([now - (n+1) * duration, now ])
.range([0, width]);
y = d3.scale.linear()
.domain([0, (1.1*d3.max(data))])
.range([height, 0]);
line = d3.svg.line()
.interpolate("basis")
.x(function (d, i) { return x(now - ((n-1) - i) * duration); })
.y(function (d, i) { return y(d); });
//initialize axis
x.axis = d3.svg.axis().scale(x).orient("top");
y.axis = d3.svg.axis().scale(y).orient("right");
x.axisT = d3.svg.axis().scale(x).orient("bottom").tickFormat("");
y.axisR = d3.svg.axis().scale(y).orient("left").tickFormat("");
//initialize svg
svg = d3.select('#chart')
.append('svg')
.style('pointer-events', 'none');
chartWrapper = svg
.append('g')
.style('pointer-events', 'all');
//cliping mask needed because trace updates and thus moves
clipMask = chartWrapper.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect");
path = chartWrapper.append('g').attr("clip-path", "url(#clip)").append('path').datum(data).classed('line', true);
xAxis = chartWrapper.append('g')
.attr("class", "x axis")
.call(x.axis);
xAxisT = chartWrapper.append('g')
.attr("class", "x axis")
.call(x.axisT);
yAxis = chartWrapper.append('g')
.attr("class", "y axis")
.call(y.axis);
yAxisR = chartWrapper.append('g')
.attr("class", "y axis")
.call(y.axisR);
yLabel = chartWrapper.append("text")
.attr("class", "y label")
//.attr("transform", "rotate(-90)")
.style("text-anchor", "middle")
.text("Temperature (°C)");
xLabel = chartWrapper.append("text")
.attr("class", "x label")
.attr("text-anchor", "middle")
.text("Time (h:min)");
chartWrapper.on('mousemove', onMouseMove)
.on("mouseover", function() { locator.style("display", null); })
.on("mouseout", function() { locator.style("display", "none"); });
//add locator
locator = chartWrapper.append('g')
.attr("class", "focus");
//.style("display", "none");
locator.append('circle')
.attr('r', 4);
locator.append("text")
.attr("x", 0)
.style("text-anchor", "middle")
.attr("dy", "-8");
touchScale = d3.time.scale()
.domain([now - (n+1) * duration, now ]);
//render the chart
render();
}
function render() {
//get dimensions based on window size
updateDimensions();
clipMask.attr("width", width)
.attr("height", height);
//update x and y scales to new dimensions
x.range([0, width]);
y.range([height, 0]);
touchScale.range([0,width]).clamp(true);
//update svg elements to new dimensions
svg
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom);
chartWrapper
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
xLabel.attr("y", height + xlabeloff )
.attr("x", ((width/2) ) );
yLabel.attr("y", - ylabeloff )
.attr("x", (margin.top - height / 2) )
.attr("transform", "rotate(-90)");
//update the axis and line
x.axis.scale(x).tickPadding(xtickpadding);
x.axisT.scale(x);
y.axis.scale(y).tickPadding(ytickpadding);
y.axisR.scale(y);
x.axis.ticks(Math.max(width/xtickdistance, 2));
y.axis.ticks(Math.max(height/ytickdistance, 2));
x.axisT.ticks(Math.max(width/xtickdistance, 2));
y.axisR.ticks(Math.max(height/ytickdistance, 2));
xAxis.attr('transform', 'translate(0,' + height + ')').call(x.axis);
xAxisT.call(x.axisT);
yAxis.call(y.axis);
yAxisR.call(y.axisR).attr('transform', 'translate(' +width +' ,0)');
path.attr('d', line);
updateTextStyle();
}
function updateTextStyle() {
winWidth = document.getElementById("widthReader").offsetWidth;
textsize = winWidth < breakPoint ? "48%" : "80%";
//scale text size
chartWrapper.selectAll("text").style("font-size", textsize);
yAxis.call(y.axis).selectAll(".tick text").style("text-anchor", "end");
}
function updateDimensions(winWidth) {
winWidth = document.getElementById("widthReader").offsetWidth;
margin.top = winWidth < breakPoint ? 4 :10;
margin.right = winWidth < breakPoint ? 4 : 10;
margin.left = winWidth < breakPoint ? 26 : 48;
margin.bottom = winWidth < breakPoint ? 22: 40;
xlabeloff = winWidth < breakPoint ? 21 : 38;
ylabeloff = winWidth < breakPoint ? 20 : 36;
xtickpadding = winWidth < breakPoint ? "-17" : "-24";
ytickpadding = winWidth < breakPoint ? "-10" : "-12";
xtickdistance = winWidth < breakPoint ? 50 : 80;
ytickdistance = winWidth < breakPoint ? 15 : 35;
width = winWidth - margin.left - margin.right;
height = .54 * width;
}
function onMouseMove() {
var x0 = x.invert(d3.mouse(this)[0]),
y0 = y.invert(d3.mouse(this)[1]);
locator.attr("transform", "translate(" + d3.mouse(this)[0] + "," + d3.mouse(this)[1] + ")");
locator.select("text").text(y0);
}
function tick() {
// update the domains
now = new Date();
touchScale.domain([now - (n-1) * duration, now ]);
x.domain([now - (n-1) * duration, now ]);
y.domain([0, (1.1*d3.max(data))]);
// push a new data point onto the back
data.push(Number($('#heater_temperature').text()));
path.attr("d", line)
.attr("transform", null);
// slide the x-axis left, rescale the y-axis
xAxisT.call(x.axisT);
xAxis.call(x.axis);
yAxis.call(y.axis);
yAxisR.call(y.axisR);
// redraw the line and slide line left
path.attr("transform", "translate(" + (x(now - (n+0) * duration) ) + " ,0)");
// pop the old data point off the front
data.shift();
updateTextStyle();
};
return {
render : render,
tick : tick,
init : init
}
})(window,d3);
$(document).ready(function() {
// Start deltaT timer to call RESTful endpoint
setInterval(function() {
$('#heater_temperature').text(Math.random()*220);
Chart.tick();
}, deltaT);
});
$(window).load(function() { //is done after document ready
window.addEventListener('resize', Chart.render);
Chart.init();
Chart.render();
});
style.css
.x.label
{
fill: #555;
}
.y.label
{
fill: #555;
}
.axis path,
.axis line {
stroke-width: 1px;
fill: none;
stroke: #ccc;
}
.axis text {
fill: #555;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1px;
}
woking js:
var transition = d3.select({}).transition()
.duration(deltaT)
.ease("linear");
function tick() {
transition = transition.each(function () {
// update the domains
now = new Date();
x.domain([now - (n-1) * duration, now ]);
y.domain([0, (1.1*d3.max(data))]);
// push a new data point onto the back
data.push(Number($('#heater_temperature').text()));
// slide the x-axis left, rescale the y-axis
xAxisT.call(x.axisT);
xAxisB.call(x.axisB);
yAxisL.call(y.axisL);
yAxisR.call(y.axisR);
// redraw the line, and slide it to the left
path.attr("d", line)
.attr("transform", " translate( " + margin.left + " ," + margin.top+" )");
// slide the line left
path.transition()
.attr("transform", "translate(" + (x(now - (n+0) * duration) + margin.left) + " ," + margin.top+ ")");
// pop the old data point off the front
data.shift();
});
}
I got it working... The problem was that i started using d3.v3 instead of d3.v2. Here is what I changed:
function tick() {
d3.transition().ease("linear").duration(deltaT-100).each(function() {
// update the domains
now = new Date(Date.now());
markerScale.domain([now - (n-2) * duration, now ]);
xScale.domain([now - (n-2) * duration, now ]);
yScale.domain([0, (1.1*d3.max(data))]);
// slide the x-axis left, rescale the y-axis
xAxisContT.transition().call(xAxisT);
xAxisCont.transition().call(xAxis);
yAxisCont.transition().call(yAxis);
yAxisContR.transition().call(yAxisR);
plot.attr("transform", null);
// push a new data point onto the back
data.push( Number($('#heater_temperature').text()) );
path.attr("d", line);
marker.data(data)
.attr("cx", xValue)
.attr("cy", yValue)
.attr("transform", null);
// redraw the line and slide plot left
plot.transition()
.attr("transform", "translate(" + (xScale(now - (n-1) * duration) ) + " ,0)");
// pop the old data point off the front
data.shift();
updateTextStyle();
});
}

Render multiple svg in d3js

Im trying to render circles in d3js. On multiply svg:s. In my HTML i have all the svg:s likte this.
<section id="a" class="render">
<svg></svg>
</section>
<section id="b" class="render">
<svg></svg>
</section>
<section id="c" class="renderr">
<svg></svg>
</section>
<section id="d" class="render">
<svg></svg>
</section>
What im trying to do is this. Depending on some values i get back from a json-call i want to render and animate the circles in different svgs.
My javascript looks like this.
for(i = 0; i<json.answers[0].length; i++) {
personal.color = json.answers[0][i].color;
personal.answer = json.answers[0][i].alternative_id;
if(json.answers[0][i].alternative_id == 1) {
drawSvg("#a")
}
if(json.answers[0][i].alternative_id == 2) {
drawSvg("#b")
}
if(json.answers[0][i].alternative_id == 3) {
drawSvg("#c")
}
if(json.answers[0][i].alternative_id == 4) {
drawSvg("#d")
}
createCircle(personal);
}
drawSvg = function(el) {
svg = d3.select(el+ " svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
force.on("tick", function(e) {
svg.selectAll("path")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
}
);
});
}
createCircle = function(p) {
people.push({
type: "circle",
size: Math.random() * 1200 + 700
});
force.start();
svg.selectAll("path")
.data(people)
.enter().append("path")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("d", d3.svg.symbol()
.size(function(d) { return d.size; })
.type(function(d) { return d.type; }))
.style("fill", p.color)
.attr("class","person")
.attr("id","p_"+index)
index++;
}

Resources