Multiple line chart issue from json - d3.js

Following is my code. I have been trying to plot humidity and dew point in basis of months in the axis. But I am getting an error of data undefined and also the month in the axis comes in number.
<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
<title> Data Visualization - Binding Dataset to Shapes Using D3 </title>
<script src="https://d3js.org/d3.v3.min.js"></script>
</head>
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.legend {
font-size: 16px;
font-weight: bold;
text-anchor: middle;
}
</style>
<body>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 70, left: 50},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%b").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var priceline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.dew); });
// Adds the svg canvas
var svg = d3.select("body")
.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 + ")");
// Get the data
d3.json("weatherdata.json", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.history.date.mon);
d.dew = +d.history.dailysummary[0].meandewptm;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.dew; }));
// Nest the entries by symbol
var dataNest = d3.nest()
.key(function(d) {return d.dew;})
.entries(data);
var color = d3.scale.category10(); // set the colour scale
legendSpace = width/dataNest.length; // spacing for legend
// Loop through each symbol / key
dataNest.forEach(function(d,i) {
svg.append("path")
.attr("class", "line")
.style("stroke", function() { // Add the colours dynamically
return d.color = color(d.key); })
.attr("d", priceline(d.values));
// Add the Legend
svg.append("text")
.attr("x", (legendSpace/2)+i*legendSpace) // spacing
.attr("y", height + (margin.bottom/2)+ 5)
.attr("class", "legend") // style the legend
.style("fill", function() { // dynamic colours
return d.color = color(d.key); })
.text(d.key);
});
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
</script>
</body>
</html>
I am not sure how will i solve it. Can anyone please help. I am sharing the data file with the current issue I am facing.
I have attached the model data the way it should look like:
Thanks in advance.

For your question on Date format for the X axis , sorry i put an answer because i can't comment (need more reputation).
I think you need to do something like this
chart.xAxis
.tickFormat(function(d) {
return d3.time.format('%b')(format(d));
});
That will display only the month.

Related

y axis ticks disappear in responsive chart in d3.js v4

I had perfectly adequate ticks in my earlier statically sized plot using d3.js v4; once I made it resizable, the ticks and values disappeared from the y axis.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test Plot Viewer</title>
<script src="js/lib/d3.v4.min.js"></script>
<script src="js/lib/jquery.min.js"></script>
<style>
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
#chart {
position: fixed;
left: 55px;
right: 15px;
top: 10px;
bottom: 55px;
}
</style>
</head>
<body>
<div id="chart"></div>
<script>
var chartDiv = document.getElementById("chart");
var svg = d3.select(chartDiv).append("svg");
// parse the date time
var parseTime = d3.timeParse("%m/%d %H:%M");
function render() {
$("svg").empty();
// Extract the width and height that was computed by CSS.
var width = chartDiv.clientWidth;
var height = chartDiv.clientHeight;
// Use the extracted size to set the size of an SVG element.
svg
.attr("width", width)
.attr("height", height);
var margin = {top: 10, right: 15, bottom: 55, left: 55};
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the line
var line = d3.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.solar); });
// Get the data
d3.csv("data_fred.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.time = parseTime(d.time);
d.solar = +d.solar;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain([0, d3.max(data, function(d) { return d.solar; })]);
// Add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", line);
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%m/%d %H:%M ")));
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y))
.ticks(10);
});
}
render();
// Redraw based on the new size whenever the browser window is resized
window.addEventListener("resize", render);
</script>
</body>
</html>
The submitter function wants more details, but I have none...
blah
blah
blah
blah
characters added to pad non-code content.
The ticks are now gone on the y axis. I've added the .tick attribute to the y axis, but no joy.
How do I get my y axis ticks back on this responsive version of the chart? TIA
Posted later: Anyone? My non-responsive version of the code is drawing correctly; "responsifying" it makes the y-axis ticks and units disappear. I've tried almost every permutation of command ordering and placement, but no luck.
Whats happening here is your Y axis ticks are getting hidden because they're not in the viewport. What you need to do is put all the elements in your svg in a <g> wrapper and translate it by left and top margins.
Here's a fiddle
var chartDiv = document.getElementById("chart");
var svg = d3.select(chartDiv).append("svg");
var g = svg.append('g');
function render() {
$('svg').empty();
// Extract the width and height that was computed by CSS.
var width = $('#chart').width();
var height = $('#chart').height();
// Use the extracted size to set the size of an SVG element.
svg
.attr("width", width)
.attr("height", height);
// set the dimensions and margins of the graph
var margin = {
top: 20,
right: 20,
bottom: 50,
left: 40
};
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
// parse the date time
var parseTime = d3.timeParse("%m/%d %H:%M");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the line
var valueline = d3.line()
.x(function(d) {
return x(d.time);
})
.y(function(d) {
return y(d.solar);
});
// Get the data
var data = [{
'time': '11/30 04:55',
'solar': -1.1
}, {
'time': '11/30 05:00',
'solar': -1.1
}, {
'time': '11/30 05:05',
'solar': -1.5
}, {
'time': '11/30 05:10',
'solar': -2
}, {
'time': '11/30 05:15',
'solar': 1
}]
// format the data
data.forEach(function(d) {
d.time = parseTime(d.time);
d.solar = +d.solar;
});
console.log(data)
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.time;
}));
var yExtent = d3.extent(data, function(d) {
return d.solar;
})
y.domain(yExtent);
g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Add the valueline path.
g.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// Add the X Axis
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%m/%d %H:%M ")))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)");
// Add the Y Axis
g.append("g")
.call(d3.axisLeft(y));
}
// d3.select("svg").remove();
// svg.remove();
// d3.selectAll("g > *").remove()
// d3.selectAll("chartDiv.path.line").remove();
// d3.select("path.line").remove();
render();
// Redraw based on the new size whenever the browser window is resized.
window.addEventListener("resize", render);
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="chart"></div>
Happy coding :)
Got it - the axis ticks were disappearing off the left edge of the window - fixed that with a transform/translate:
// Add the Y Axis
svg.append("g")
.attr("transform", "translate(40 ,10)")
.call(d3.axisLeft(y));
...with a similar translation of the x axis and path to match.
Also, the axis scale now appeared with an extent of 0 to 1.0, as it wasn't being passed out of the file read loop since it was an asynchronous operation. Bringing the svg.append's into the data read loop restored my "normal" units to the axis.

d3 js with scale and zoom in zoom out

Using below d3 js code draw scale, but not able to figure out how i can convert x-axis dates to hours on zoom, trying to achieve x-axis dates get converted into hours on zoom in and on zoom out again into dates.
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// Adds the svg canvas
var svg = d3.select("body")
.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 + ")")
.call(d3.behavior.zoom().on("zoom", function () {
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")")
}))
.append("g");
// Get the data
d3.csv("data.csv", function(error, data) {
data = [{
date:"1-May-12",
close:"58.13"
},
{
date:"30-Apr-12",
close:"53.98"
},{
date:"27-Apr-12",
close:"67.00"
},{
date:"26-Apr-12",
close:"89.70"
}
]
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
});
</script>
</body>
In a callback that implements the zoom event. You only transform the svg object. you have to process axis also. It seems that you have an effect of axis zooming.
xScale = d3.event.transform.rescaleX(xScale)
gX.call(xAxis.scale(xScale))
It rescales the scaler according to the zoom event and than the axis is rescaled and is applied to the axis group container.

d3 bar chart using rangeRoundBands - why is there outer padding?

I'm creating a bar chart using an ordinal scale for the x axis, and using rangeRoundBands. I'm following this example: https://bl.ocks.org/mbostock/3885304
However, my chart has outer padding -- big spaces at the beginning and end of the axis, where I'd like the bars to fully extend. The screen shot below shows the spaces I'm referring to circled in red.
How can I remove these spaces? I need my margins and the svg width and height to remain the same.
Here is a Plunker with the chart as it is now:
https://plnkr.co/edit/gMw7jvieKSlFbHLTNY9o?p=preview
Code is also below:
<!doctype html>
<html>
<head>
<style>
.axis path{
fill: none;
stroke: #cccccc;
stroke-width: 2px;
}
.x.axis text{
display:none;
}
.bar{
fill: blue;
}
body{
font-family:Helvetica, Arial, sans-serif;
}
</style>
</head>
<body>
<div id="barchart"></div>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 0, bottom: 50, left: 300},
width = 800 - margin.left - margin.right;
height = 465 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .5);
var y = d3.scale.linear().range([height, 0]);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom");
var barsvg = d3.select("#barchart")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "barchartbox")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Get the data
d3.json("population.json", function(error, data1) {
x.domain(data1.map(function(d) { return d.country; }));
y.domain([0, d3.max(data1, function(d) { return d.value; })]);
barsvg.selectAll(".axis").remove();
// Add the Y Axis
barsvg.append("g")
.attr("class", "y axis")
.call(yAxis);
// Add the X Axis
barsvg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
var bars = barsvg.selectAll(".bar")
.data(data1);
bars.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.country); })
.attr("width", x.rangeBand())
.attr("y", function(d) {return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
bars.exit().remove();
});
</script>
</body>
</html>
You doing.
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .5);
Instead use rangeBands.
var x = d3.scale.ordinal()
.rangeBands([0, width], .5);
working code here
It is specified in the d3 document that while using rangeRoundBands, rounding introduces additional outer padding which is, on average, proportional to the length of the domain.
For example, for a domain of size 50, an additional 25px of outer padding on either side may be required. Modifying the range extent to be closer to a multiple of the domain length may reduce the additional padding.
Reference: rangeRoundBands
So the solution would be to use following lines after setting the x axis domain:
var mult = Math.max (1, Math.floor (width / x.domain().length));
x.rangeRoundBands ([0, (x.domain().length * mult)], 0.1, 0);

Styling Axis Data in D3.js

I have this piece of code which fetches data from MySQL and plots the graph, and it does it correctly. However, the marking segments such as the time span coming up in text appears to be in black. I've tried doing many changes but it was futile. I want to make it white.
Also, I want the x-axis to start with January and not 2015 as seen in the image.
<style>
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 900 - margin.left - margin.right,
height = 540 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%Y-%m-%d").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.lsdate); })
.y(function(d) { return y(d.units); });
// Adds the svg canvas
var svg = d3.select("body")
.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 + ")");
// Get the data
d3.json("common/scripts/charts.php", function(error, data) {
data.forEach(function(d) {
d.lsdate = parseDate(d.lsdate);
d.units = +d.units;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.lsdate; }));
y.domain([0, d3.max(data, function(d) { return d.units; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
Screenshot:
As per #Lars's comment, you need to use the fill attribute to apply style to your text elements:
.axis text {
fill: white;
}
Working fiddle here: http://jsfiddle.net/henbox/jr6pk4bj/1/
Aside: To prevent the x-axis starting with the year, but to always use month names, add this line when you define xAxis:
.tickFormat(d3.time.format("%B"));
Here's an update to the fiddle showing that change too: http://jsfiddle.net/henbox/jr6pk4bj/2/

TypeError: string is undefined (var c, p, i = 0, n = template.length, m = string.length;)

I am starting with this simple example but I have the following error (using Firebug)
TypeError: string is undefined
[Break On This Error]
var c, p, i = 0, n = template.length, m = string.length;
Any hint?? (I tried similar responses and it didn't work, I guess there is something wrong with the time and date format, but I believe the "%Y-%m-%d" format is the right one that I have in the csv file...)
Thanks!!
//here my prueba.html file
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body{font: 12px arial;}
path{stroke: steelblue;
stroke-width: 2;
fill: none;}
.axis path,
.axis line {fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;}
</style>
<body>
<script type="text/javascript" src="d3/d3.v3.js"></script>
<script>
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y-%m-%d").parse; // HERE ERROR !!!
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var valueline = d3.svg.line()
.x(function(d) { return x(d.timestamp); })
.y(function(d) { return y(d.temperature); });
var svg = d3.select("body")
.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 +")");
// Get the data
d3.tsv("data/data.csv", function(error, data)
{data.forEach(function(d)
{d.timestamp = parseDate(d.timestamp); // HERE ERROR !!!
d.temperature = +d.temperature;});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
svg.append("path") // Add the valueline path.
.attr("d", valueline(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
});
</script>
</body>
//here my data.csv file
timestamp,temperature
1900-01-01,20
1900-01-02,20
1900-01-03,17
1900-01-04,23
1900-01-05,15
1900-01-06,22
1900-01-07,24
1900-01-08,15
1900-01-09,25
1900-01-10,19
1900-01-11,23
1900-01-12,19
1900-01-13,17
1900-01-14,15
1900-01-15,17
1900-01-16,21
1900-01-17,23
1900-01-18,25
1900-01-19,17
1900-01-20,22
1900-01-21,23
1900-01-22,17
1900-01-23,15
1900-01-24,23
1900-01-25,19
1900-01-26,25
1900-01-27,21
1900-01-28,22
1900-01-29,20
1900-01-30,15
1900-01-31,21
1900-02-01,19
1900-02-02,15
1900-02-03,15
1900-02-04,25
1900-02-05,23
1900-02-06,25
1900-02-07,15
1900-02-08,18
1900-02-09,22
1900-02-10,15
1900-02-11,19
1900-02-12,18
1900-02-13,24
1900-02-14,22
1900-02-15,16
1900-02-16,21
1900-02-17,24
1900-02-18,25
A picture of this using Chrome developer tools...
http://i.imgur.com/0vZhCgK.jpg?1
You are using d3.tsv, you should be using d3.csv instead.
Also if you want to use tsv (Tab-separated values) you'll need to replace every comas by a tabulation.
PS: care to not save the file with an editor that automatically replace the tabulations with spaces :)

Resources