I read that JSON is built on two structures.
I'm able to make a chart from a JSON list but the problem with CakePHP is that the output is in JSON object format.
How can I fetch my JSON object with D3 script?
(I use d3.v4.min.js)
data.json
{
"result": [
{
"owner": "0",
"variable": "Setpoint",
"date": "2016-09-28T19:24:19-0400",
"datas": 20
},
{
"owner": "0",
"variable": "Setpoint",
"date": "2016-09-25T10:07:42-0400",
"datas": 20
}
]
}
Script
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parse the date / time
var parseTime = d3.timeParse("%Y-%m-%dT%H:%M:%S%Z");
// 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.date); })
.y(function(d) { return y(d.datas); });
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#debug").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("http://localhost/badina/datas/d3/0/Setpoint.json", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.datas = +d.datas;
});
// 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.datas; })]);
// Add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
});
var data = data.result
d3.json("http://localhost/badina/data1.json", function(error, data) {
if (error) throw error;
var data = data.result
console.log(data);
// format the data
data.forEach(function(d) {
Related
When building an area chart in D3.js, when you have only a single value the chart does not render.
For demonstration purposes, I modified the following example: https://d3-graph-gallery.com/graph/area_basic.html to illustrate the problem.
<script>
// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 30, left: 50},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/3_TwoNumOrdered_comma.csv",
// When reading the csv, I must format variables:
function(d){
return { date : d3.timeParse("%Y-%m-%d")(d.date), value : d.value }
},
// Now I can use this dataset:
function(data) {
data = [data[0]]
// Add X axis --> it is a date format
var x = d3.scaleTime()
.domain(d3.extent(data, function(d) { return d.date; }))
.range([ 0, width ]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return +d.value; })])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y));
// Add the area
svg.append("path")
.datum(data)
.attr("fill", "#cce5df")
.attr("stroke", "#69b3a2")
.attr("stroke-width", 1.5)
.attr("d", d3.area()
.x(function(d) { return x(d.date) })
.y0(y(0))
.y1(function(d) { return y(d.value) })
)
})
</script>
I would expect that chart to look something like:
If you inspect the element the path element you can see it is rendering, just 0 width/height:
I have a csv file "data_good.csv" with the simple format : date,value ("date" here is just a number, not date format.)
I've just started using d3js and here is the code i'm using for the graph :
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = screen.width - 200,
height = 200;
// set the ranges
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#body_d3_graph").append("svg").attr("class","my_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.csv("tmp/data_good.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.value = +d.value;
});
// Scale the range of the data in the domains
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.style("fill", "green")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
});
It's working as expected with several green bars.
But now i have another csv file ("tmp/data_bad.csv") and i would like to have it as a red bar on top of the green one. According to their key "date". (There is no date without value : data_good.csv and data_bad.csv have the same number of lines.)
I think that you should separate logic which draws chart and logic which loads a file.
Also you have to group bars and separate drawing axes from bars.
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = screen.width - 200,
height = 200;
// set the ranges
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#body_d3_graph").append("svg").attr("class","my_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 drawAxes = function(data) {
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
}
var drawChart = function(data) {
// Scale the range of the data in the domains
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
// append the rectangles for the bar chart
svg
.append('g')
.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.style("fill", "green")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
}
// get the data
d3.csv("tmp/data_good.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.value = +d.value;
});
drawChart(data);
});
Then if you want to preserve domain scaling to be defined only by "good data" you can abstract it too and use only in "good data reader".
Remember to draw axes when changing scales.
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = screen.width - 200,
height = 200;
// set the ranges
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#body_d3_graph").append("svg").attr("class","my_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 drawAxes = function(data) {
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
}
var setDomains = function(data) {
// Scale the range of the data in the domains
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
drawAxes();
}
var drawChart = function(data) {
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.style("fill", "green")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
}
// get the data
d3.csv("tmp/data_good.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.value = +d.value;
});
setDomains(data);
drawChart(data);
});
d3.csv("tmp/data_bad.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.value = +d.value;
});
drawChart(data);
});
I am trying to create a line chart but I get the error...
Error: attribute d: Expected number, "M67,0L67,0LNaN,0LNaN,0L728"
... every time I have three or more elements. I want to set the date with that format in the x-axis. I have tried scaling with times, but I just want to show the dates that the JSON file contains, not a range of dates.
This is the JSON file I am using:
[{"date": "20-Jun-19", "close": "5", "text": "Test"},
{"date": "21-Jun-19", "close": "5", "text": "Test"},
{"date": "25-Jun-19", "close": "5", "text": "Test"}]
This is the Javascript I am using.
var label = d3.select(".label");
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 1460 - margin.left - margin.right,
height = 870 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(20);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(15);
// 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(".anxiety-graphic")
.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 +
")");
d3.json("data.json", function(error, data) {
var categoriesNames = data.map(function (d) {
return +d.date;
});
x.domain(categoriesNames);
// Scale the range of the data
x.domain(d3.extent(data, function(d) { console.log(d); return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the valueline path.
svg.append("path") // Add the valueline path.
.attr("class", "line")
.attr("d", valueline(data));
// Add the valueline path.
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", 10)
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.close)
})
.on("mouseover", function(d,i) {
label.style("transform", "translate("+ x(d.date) +"px," + (y(d.close)) +"px)")
label.text(d.close)
});
// Add the X Axis
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
});
For whatever reason, you are replacing the correct domain...
x.domain(categoriesNames);
... for a wrong one in the very next line:
x.domain(d3.extent(data, function(d){ return d.date; }));
d3.extent returns an array with 2 values only, and that's why you're getting this issue when your data have three or more elements.
Also, the map for creating categoriesNames has an issue:
var categoriesNames = data.map(function (d) {
return +d.date;
});
Since the date is a string containing letters, it's not clear why you're using the unary plus (which will return NaN). Drop that:
var categoriesNames = data.map(function (d) {
return d.date;
});
Here is your code with those changes:
var label = d3.select(".label");
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 1460 - margin.left - margin.right,
height = 870 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(20);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(15);
// 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 +
")");
var data = [{
"date": "20-Jun-19",
"close": "5",
"text": "Test"
},
{
"date": "21-Jun-19",
"close": "5",
"text": "Test"
},
{
"date": "25-Jun-19",
"close": "5",
"text": "Test"
}
];
var categoriesNames = data.map(function(d) {
return d.date;
});
x.domain(categoriesNames);
y.domain([0, d3.max(data, function(d) {
return d.close;
})]);
// Add the valueline path.
svg.append("path") // Add the valueline path.
.attr("class", "line")
.attr("d", valueline(data));
// Add the valueline path.
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", 10)
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.close)
})
.on("mouseover", function(d, i) {
label.style("transform", "translate(" + x(d.date) + "px," + (y(d.close)) + "px)")
label.text(d.close)
});
// Add the X Axis
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
I'm using angularjs to get data from facebook
I have written following code.
At the backend,it is giving proper output but for $scope.chart variable but at the front end when I log the data it is showing date:null.I'm uploading images for resprctive data.Backend Data Imagefrontend data showing date Null
<script>
function mychart(){
var dom_el = document.querySelector('[ng-controller="myCtrl"]');
var ng_el = angular.element(dom_el);
var ng_el_scope = ng_el.scope();
var data = ng_el_scope.chart;
//var data = chart;
console.log(data);
// 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 + ")");
// Get the data
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
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
}
</script>
I am trying to create a line graph using D3. The data for the graph is stored in an XML file like this:
XML Code:
<record>
<date_value>1376092800000</date_value>
</record>
JavaScript Code:
var record=xmlDoc.getElementsByTagName("record");
// 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 + ")");
// Get the data
d3.xml("values.xml", "application/xml", function(data) {
for(var d = 0; d < record.length; d++){
d.date = parseDate(record[d].getElementsByTagName("date_value")[0].childNodes[0].nodeValue);
d.close += 1;
};
// 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
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
I'm having trouble with the part where I get the data. The x value is the date value and the y value should be increased by one each time. With the current code I have no errors, but only the y axis is showing with no data. I would appreciate any suggestions. Thanks!