D3.JS bar graph in Firefox cshtml - d3.js

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)

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>

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.

shapes overlay instead of transition in d3.js

I tried to have a beeswarm plot shift datapoint location to no avail. First, I made a simple scatter plot which can transition except x axis overlays on toggling.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: Transitioning points to randomized values, plus rescaled axes!</title>
<script src="https://d3js.org/d3.v4.min.js"></script><style type="text/css">
/* No style rules here yet */
</style>
</head>
<body>
<label>
<input type="radio" name='market' value="a" checked/>a
<input type="radio" name='market' value="b"/>b
</label>
<p>Click on this text to update the chart with new data values as many times as you like!</p>
<script type="text/javascript">
//Width and height
var w = 500;
var h = 300;
var padding = 30;
//Dynamic, random dataset
dataset = [
{"id":1,
"value":20,
"group":"a"},
{"id":1,
"value":10,
"group":"a"},
{"id":1,
"value":30,
"group":"a"},
{"id":1,
"value":40,
"group":"a"},
{"id":1,
"value":42,
"group":"a"},
{"id":1,
"value":10,
"group":"b"},
{"id":1,
"value":12,
"group":"b"},
{"id":1,
"value":15,
"group":"b"},
{"id":1,
"value":23,
"group":"b"},
{"id":1,
"value":22,
"group":"b"},
{"id":1,
"value":54,
"group":"b"}
]
//Create scale functions
var xScale = d3.scaleLinear()
.range([padding, w - padding * 2]);
var yScale = d3.scaleLinear()
.range([h - padding, padding]);
//Define X axis
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5);
//Define Y axis
var yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
draw(dataset.filter(d=> d.group=="a"));
//////////////////
//toggle//
//////////////////
d3.selectAll("input")
.on("change", function() {
var data_new = dataset.filter(d => (d.group == this.value));
draw(data_new);
});
//////////////////
//Create circles
//////////////////
function draw(dataset) {
//////////////////
//Create axis
xScale.domain([0, d3.max(dataset, function(d) { return d.value; })])
yScale.domain([0, 2])
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5);
//Create X axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.transition(2000)
.call(xAxis);
//Create Y axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
//////////////////
//draw circle
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d.value);
})
.attr("cy", function(d) {
return yScale(d.id);
})
.attr("r", 3)
.attr("opacity",0.2);
//update
svg.selectAll("circle")
.transition()
.duration(1000)
.attr("cx", function(d) {
return xScale(d.value);
})
}
</script>
</body>
</html>
However when apply a similar code for beeswarm plot, the points don't shift location on selection, they just layer on, like the x axis in the first example:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<label>
<input type="radio" name='market' value="a" checked/>a
<input type="radio" name='market' value="b"/>b
</label>
<svg width="400" height="200"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 40, right: 40, bottom: 40, left: 40},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom;
var formatValue = d3.format(",d");
var x = d3.scaleLog()
.rangeRound([0, width]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
data = [
{"id":1,
"value":20,
"group":"a"},
{"id":1,
"value":21,
"group":"a"},
{"id":1,
"value":30,
"group":"a"},
{"id":1,
"value":32,
"group":"a"},
{"id":1,
"value":42,
"group":"a"},
{"id":1,
"value":10,
"group":"b"},
{"id":1,
"value":12,
"group":"b"},
{"id":1,
"value":15,
"group":"b"},
{"id":1,
"value":23,
"group":"b"},
{"id":1,
"value":22,
"group":"b"},
{"id":1,
"value":24,
"group":"b"}
]
//default
draw(data.filter(d=> d.group=="a"));
d3.selectAll("input")
.on("change", function()
{
var newdata = draw(data.filter(d=> d.group==this.value));
draw(newdata)
} )
/////////////////
//draw swarmplot
/////////////////
function draw(data) {
// transition
var t = d3.transition()
.duration(750);
x.domain(d3.extent(data, d=> d.value));
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) { return x(d.value); }).strength(1))
.force("y", d3.forceY(height / 3))
.force("collide", d3.forceCollide(18))
.stop();
for (var i = 0; i < 120; ++i) simulation.tick();
//axis
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(20, ".0s"));
//for mouse-over
var cell = g.append("g")
.attr("class", "cells")
.selectAll("g").data(d3.voronoi()
.extent([[-margin.left, -margin.top], [width + margin.right, height + margin.top]])
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.polygons(data)).enter().append("g");
//circle
cell.append("circle")
.attr("r", 3)
.attr("cx", function(d) { return d.data.x; })
.attr("cy", function(d) { return d.data.y; })
.attr("fill", d => (d.data.Food_Sub_Group))
.attr("opacity", 0.4);
//update circle
cell.selectAll("circle")
.transition()
.duration(1000)
.attr("cx", function(d) { return d.data.x; })
}
</script>
Anything amiss here? Thanks.
Here is the difference:
In draw function of the first code, you bind data to 'circle' elements. If you have 5 circle elements before and new data size is 5, then no element is added, what changed is only data. If you have 5 circles before and new data size is 6, then with 'enter()' and 'append' function, after execution, you will have 6 elements.
In draw function of the second code, you add new elements. Every call to draw function adds new points. If you have 5 circle elements before and new data size is 5, then 5 elements are added, you will have 10 elements.
What you say 'no shift location' is actually multiple points be plotted in same place(the point seems to be darker) or different places(more points).
See data binding in d3 which may help.

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();
});
}

Providing X axis with proper tick values

<!DOCTYPE html>
<html lang="en">
<head>
<style>
.axis path {
fill: none;
stroke: #777;
shape-rendering: crispEdges;
}
.axis text {
font: 10px sans-serif;
}
</style>
</head>
<body>
<svg id="visualization" width="1000" height="500"></svg>
</body>
</html>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
function InitChart() {
var data = [{
"sale": "215",
"calendarDate": "20140302"
}, {
"sale": "179",
"calendarDate": "20140303"
}, {
"sale": "199",
"calendarDate": "20140304"
}, {
"sale": "134",
"calendarDate": "20140305"
}, {
"sale": "176",
"calendarDate": "20140306"
}];
var vis = d3.select("#visualization"),
WIDTH = 1000,
HEIGHT = 500,
MARGINS = {
top: 20,
right: 20,
bottom: 20,
left: 50
},
calendarDatesAsStrings = ['02.03.2014','03.03.2014','04.03.2014','05.03.2014','06.03.2014'],
xScale = d3.scale.linear().range([MARGINS.left, WIDTH - MARGINS.right]).domain([20140302, 20140306]),
yScale = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([124, 225]),
xAxis = d3.svg.axis().scale(xScale),
yAxis = d3.svg.axis().scale(yScale).orient("left");
vis.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
.call(xAxis)
.selectAll('text')
.text(function (d, i) { return calendarDatesAsStrings[i]; });
vis.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(" + (MARGINS.left) + ",0)")
.call(yAxis);
var lineGen = d3.svg.line()
.x(function(d) {
return xScale(d.calendarDate);
})
.y(function(d) {
return yScale(d.sale);
});
vis.append('svg:path')
.attr('d', lineGen(data))
.attr('stroke', 'green')
.attr('stroke-width', 2)
.attr('fill', 'none');
}
InitChart();
</script>
http://jsbin.com/zupejizeho/1/edit
I expected the labels to be in their right positions, they aren't.
Instead, they look like they are "compressed" and placed every half-segment instead of full segment.
What am I doing wrong?
You have a decision to make here. Treat your dates as strings and use an ordinal scale or you treat your dates as dates and use a time scale. Right now you are using a linear scale which does not map nicely to date stamps.
Since your dataset is pretty limited an ordinal scale is probably easier:
// map all my dates to an ordinal
// with points separated nicely in my range
xScale = d3.scale.ordinal()
.rangePoints([MARGINS.left, WIDTH - MARGINS.right])
.domain(data.map(function(d){ return d.calendarDate })),
And format the date strings:
// then reformat the strings to what you want with some format helpers
oldFormat = d3.time.format("%Y%m%d"),
newFormat = d3.time.format("%m.%d.%Y");
xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickFormat(function(d){
var dateTime = oldFormat.parse(d); // parse the string to date
return newFormat(dateTime); // format it back to appropriate string
}),
Updated example here.
If you get a little more data complexity, you'll probably want to shift to really treating your dates as dates. So, first, fix your data:
data = data.map(function(d){
return {
sale: +d.sale, // this is really a numeric
calendarDate: oldFormat.parse(d.calendarDate) // this be a datetime now
}
});
Set up your scale with time:
xScale = d3.time.scale()
.range([MARGINS.left, WIDTH - MARGINS.right])
.domain(
[d3.min(data.map(function(d){ return d.calendarDate })),
d3.max(data.map(function(d){ return d.calendarDate }))]
),
And the format becomes simpler:
xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickFormat(d3.time.format("%m.%d.%Y")),
Here an example of this.
you don't need to add the last two rows after the call of the xAxis, here:
vis.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
.call(xAxis)
.selectAll('text')
.text(function (d, i) { return calendarDatesAsStrings[i]; });
you simply have to call the xAxis and that's it, working link: http://jsbin.com/yabecihihi/1/edit?html,output (the dates need to be parsed properly with a time scale tho)

Resources