Time always becoming epoch-time and out of domain range - javascript

I have this kind of JSON
{"Arsitek":[{"tanggal":"2015-01-01","jumlah":286},{"tanggal":"2015-02-01","jumlah":601},{"tanggal":"2015-03-01","jumlah":845},{"tanggal":"2015-04-01","jumlah":550},{"tanggal":"2015-05-01","jumlah":500},{"tanggal":"2015-06-01","jumlah":201},{"tanggal":"2015-07-01","jumlah":73},{"tanggal":"2015-08-01","jumlah":503},{"tanggal":"2015-09-01","jumlah":884},{"tanggal":"2015-10-01","jumlah":782},{"tanggal":"2015-11-01","jumlah":393},{"tanggal":"2015-12-01","jumlah":150}],"DKV":[{"tanggal":"2015-01-01","jumlah":94},
I am using fetch API, and then passing the JSON to this code
function perbulan(dataset,target,terlambat){
//if(terlambat)target+='Terlambat'
document.getElementById(target).innerHTML=''
let width = 1100,height = 650
let color = d3.scaleOrdinal().range(['#334455', '#6c1f22', '#e3e8e1', '#6b112f', '#3C2F77',
'#123456', '#22c1f6', '#3e7e9e', '#b6f121', '#32C6EA',
'#A60F2B', '#648C85', '#B3F2C9', '#528C18', '#C3F25C',
'#6Af0B2', '#46C858', '#3B2F9C', '#25C881', '#3C2FC5',
'#8A23B1', '#2A81B3', '#BADCFE', '#917355', '#A1B9F5'])
let parsetime = d3.timeParse("%Y-%m-%d")
let svg = d3.select(`#${target}`)
.append('svg')
.attr('width', width)
.attr('height', height)
let xS = d3.scaleTime()
let yS = d3.scaleLinear()
let valueline = d3.line().x(d=>d.tanggal).y(d=>d.jumlah)
let axis=true;
Object.keys(dataset).forEach((data,i)=>{
let res=dataset[data].map(x=>({'jumlah':+x.jumlah,'tanggal':parsetime(x.tanggal)}))
//console.log(res)
xS.domain([d3.min(res, d=>d.tanggal),d3.max(res, d=>d.tanggal)]).range([100,1000])
yS.domain([0,d3.max(res, d=>d.jumlah)]).range([600,20])
svg.append("path")
.datum(res)
.attr("d", valueline)
.attr("class","line")
.style("stroke", color(i))//change to iterator later
if(axis){
svg.append('g')
.attr("transform", "translate(0,600)")
.call(d3.axisBottom(xS))
svg.append("g")
.attr("transform", "translate(100,-1)")
.call(d3.axisLeft(yS))
axis=false
}
})
}
But the line won't show, instead when I inspected the element it shows:
<path d="M1420045200000,286L1422723600000,601L1425142800000,845L1427821200000,550L1430413200000,500L1433091600000,201L1435683600000,73L1438362000000,503L1441040400000,884L1443632400000,782L1446310800000,393L1448902800000,150" class="line" style="stroke: rgb(51, 68, 85);"></path>
When I highlight it, the line is at coordinate 28857700000x811, so it is out of range.
I tried to manually use new Date(d.tanggal), but it does nothing.
Any ideas?

You have to use your scales. Right now, there is no scale in the line generator:
let valueline = d3.line().x(d=>d.tanggal).y(d=>d.jumlah)
Instead of that, it should be:
let valueline = d3.line().x(d => xS(d.tanggal)).y(d => yS(d.jumlah))
//scales here--------------------^---------------------^
Here is your code with that change only:
var dataset = {
"Arsitek": [{
"tanggal": "2015-01-01",
"jumlah": 286
}, {
"tanggal": "2015-02-01",
"jumlah": 601
}, {
"tanggal": "2015-03-01",
"jumlah": 845
}, {
"tanggal": "2015-04-01",
"jumlah": 550
}, {
"tanggal": "2015-05-01",
"jumlah": 500
}, {
"tanggal": "2015-06-01",
"jumlah": 201
}, {
"tanggal": "2015-07-01",
"jumlah": 73
}, {
"tanggal": "2015-08-01",
"jumlah": 503
}, {
"tanggal": "2015-09-01",
"jumlah": 884
}, {
"tanggal": "2015-10-01",
"jumlah": 782
}, {
"tanggal": "2015-11-01",
"jumlah": 393
}, {
"tanggal": "2015-12-01",
"jumlah": 150
}]
};
let width = 1100,
height = 650
let color = d3.scaleOrdinal().range(['#334455', '#6c1f22', '#e3e8e1', '#6b112f', '#3C2F77', '#123456', '#22c1f6', '#3e7e9e', '#b6f121', '#32C6EA', '#A60F2B', '#648C85', '#B3F2C9', '#528C18', '#C3F25C', '#6Af0B2', '#46C858', '#3B2F9C', '#25C881', '#3C2FC5', '#8A23B1', '#2A81B3', '#BADCFE', '#917355', '#A1B9F5'])
let parsetime = d3.timeParse("%Y-%m-%d")
let svg = d3.select("body")
.append('svg')
.attr('width', width)
.attr('height', height)
let xS = d3.scaleTime()
let yS = d3.scaleLinear()
let valueline = d3.line().x(d => xS(d.tanggal)).y(d => yS(d.jumlah))
let axis = true;
Object.keys(dataset).forEach((data, i) => {
let res = dataset[data].map(x => ({
'jumlah': +x.jumlah,
'tanggal': parsetime(x.tanggal)
}))
//console.log(res)
xS.domain([d3.min(res, d => d.tanggal), d3.max(res, d => d.tanggal)]).range([100, 1000])
yS.domain([0, d3.max(res, d => d.jumlah)]).range([600, 20]);
svg.append("path")
.datum(res)
.attr("d", valueline)
.attr("class", "line")
.style("stroke", color(i)) //change to iterator later
if (axis) {
svg.append('g')
.attr("transform", "translate(0,600)")
.call(d3.axisBottom(xS))
svg.append("g")
.attr("transform", "translate(100,-1)")
.call(d3.axisLeft(yS))
axis = false
}
})
.line {
fill: none;
stroke: teal;
stroke-width: 2px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

Related

D3JS interpolateSpectral

I'm not sure I understand how to use D3's interpolateSpectral to get my color scheme. When I attempt the following my colors work, the problem is I don't know how many potential colors I could need so I wanted to use interpolateSpectral to get as many as my dataset asks for.
When I do this
const color = d3.scaleOrdinal(d3.schemeCategory10);
g.selectAll('.chart-arc')
.data(pie(data))
.enter()
.append('path')
.attr('class', 'chart-arc')
.attr('d', arc)
.style('fill', d => console.log(color(d.data.label)))
.on('mouseover', this.mouseover.bind(this))
.on('mousemove', this.mousemove.bind(this))
.on('mouseout', this.mouseout.bind(this))
;
Those colors work as I can see them logged to my console.
When I do this...
const color = d3.scaleSequential(d3.interpolateSpectral);
g.selectAll('.chart-arc')
.data(pie(data))
.enter()
.append('path')
.attr('class', 'chart-arc')
.attr('d', arc)
.style('fill', d => console.log(color(d.data.label)))
.on('mouseover', this.mouseover.bind(this))
.on('mousemove', this.mousemove.bind(this))
.on('mouseout', this.mouseout.bind(this))
;
The the console logs all instances of color as undefined.
UPDATE
This is the full code for the vue.js/d3js page.
<template>
<div>
<div v-if="!loading" id="chart"></div>
<div v-if="loading">Loading...</div>
</div>
</template>
<script>
import * as d3 from 'd3';
import axios from "axios";
export default {
name: "piechart2",
data(){
this.loadData()
.then((theData) => (
this.data = theData,
this.createChart
))
.finally(() => (
this.loading = false,
this.loadChart()
));
return{
data: "",
element: 'body',
width: 600,
height: 400,
loading: true
}
},
methods: {
loadData: function(){
return axios.get('http://localhost:8080/mockdata/piemock.json')
.then(result => { return result; })
.catch(error => { console.error(error); throw error; });
},
loadChart: function () {
let newData = this.data.data.map(
obj => {
return {
value: obj.count,
label : obj.label
}
}
);
let newJson = {data: newData, width: this.width, height: this.height, element: this.element}
var {data, width, height, element} = newJson;
const svg = d3.select(element)
.append('svg')
.attr('class', 'chart-svg')
.attr('width', width)
.attr('height', height)
;
this.tooltip = d3.select(element)
.append('div')
.attr('class', 'tooltip')
.style('display', 'none')
;
// const color = d3.scaleOrdinal(d3.schemeCategory10)
// ;
// console.log(color(1))
console.log(data.length)
const color = d3.scaleSequential(d3.interpolateSpectral).domain([0, data.length]);//d3.scaleSequential(d3.interpolateSpectral);
// console.log(color2(1))
const r = Math.min(width, height) / 3;
const arc = d3.arc()
.innerRadius(0)
.outerRadius(r)
;
const pie = d3.pie()
.value(d => d.value)
;
const g = svg.append('g')
.attr('transform', `translate(${width / 2},${height / 2})`)
;
g.selectAll('.chart-arc')
.data(pie(data))
.enter()
.append('path')
.attr('class', 'chart-arc')
.attr('d', arc)
.style('fill', d => console.log(color(d.data.label)))
.on('mouseover', this.mouseover.bind(this))
.on('mousemove', this.mousemove.bind(this))
.on('mouseout', this.mouseout.bind(this))
;
const l = svg.append('g')
.attr('transform', `translate(0,${height - 20})`);
const xl = d3.scaleBand()
.range([0, width])
.padding(0.3)
.domain(data.map(d => d.label))
;
const legend = l.selectAll('.chart-legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'chart-legend')
.attr('transform', (d) => `translate(${xl(d)},0)`)
;
legend.append('rect')
.attr('width', 12)
.attr('height', 12)
.style('fill', color)
;
legend.append('text')
.attr('x', 20)
.attr('y', 10)
.text(d => d)
;
},
mouseover() {
this.tooltip
.style('display', 'inline-block')
.style('position', 'absolute')
;
},
mousemove() {
this.tooltip
.text([d3.event.pageX, d3.event.pageY].join(','))
.style('left', d3.event.pageX + 10 + "px")
.style('top', d3.event.pageY + 10 + "px")
;
},
mouseout() {
this.tooltip
.style('display', 'none')
;
},
render() {
// move rendering logic down here
}
}
}
</script>
<style>
.tooltip {
background-color: rgba(0,0,0,0.75);
padding: 15px;
border-radius: 2px;
font-family: sans-serif;
color: white;
pointer-events: none;
box-shadow: 0 0 5px #999999;
}
.chart-svg {
border: 1px solid #ddd;
}
.chart-legend {
font-family: 'Open Sans', sans-serif;
}
</style>
Here is the JSON
[{"label": "Assamese", "count": 13},
{"label": "Bengali", "count": 83},
{"label": "Bodo", "count": 1.4},
{"label": "Dogri", "count": 2.3},
{"label": "Gujarati", "count": 46},
{"label": "Hindi", "count": 300},
{"label": "Kannada", "count": 38},
{"label": "Kashmiri", "count": 5.5},
{"label": "Konkani", "count": 5},
{"label": "Maithili", "count": 20},
{"label": "Malayalam", "count": 33},
{"label": "Manipuri", "count": 1.5},
{"label": "Marathi", "count": 73},
{"label": "Nepali", "count": 2.9},
{"label": "Oriya", "count": 33},
{"label": "Punjabi", "count": 29},
{"label": "Sanskrit", "count": 0.01},
{"label": "Santhali", "count": 6.5},
{"label": "Sindhi", "count": 2.5},
{"label": "Tamil", "count": 61},
{"label": "Telugu", "count": 74},
{"label": "Urdu", "count": 52}]
Sequential scales do take exactly two numeric values as input domain (source).
This means that the input should be a number, and output a color.
It seems that in the question, an attempt is made to use text labels as input, this is causing the problem.
It is not recommended to use a sequential color scale to map unrelated value: The color scale denotes "proximity" between values, and will be used on labels which are not necessarily connected / similar.
In case the 12 colors categorical scale schemeSet3 from d3-scale-chromatic is not enough, a scale can be generated with a tool like iWantHue, optimizing the chances of having differentiable colors.
If it is decided to stay with the approach of slicing colors from d3.interpolateSpectral, then this notebook can be used as a reference. It illustrates how d3-scale-chromatic schemes can be used to map discrete values (switch the selection menu to Discrete(n) rather than Continuous to see the code in action).
The approach would be to map each label to a slice of the color scheme:
// assumption: an array called `labels` has been created, containing the unique label values
let n = labels.length
, colorbyValue = {}
for (let i = 0; i < n; ++i) {
colorbyValue[labels[i]] = d3.rgb(d3.interpolateSpectral(i / (n - 1))).hex();
}
// then use with colorByValue[d.data.label]
You are missing a domain, try the following:
const color = d3.scaleSequential(d3.interpolateSpectral).domain([0, data.length]);

d3js bar graph with x+y axes: x axis value distribution

I'm trying to show a vertical bar chart with x and y axes. I get the bar chart with y axis, however I'm struggling with the x-axis.
The x-axis text labels are equally distributed with the width of the bars, however: there are markers/vertical lines on the x-axis with varying width, particularly the first and last sections, even though I've specified the scaleBand and the domain.
My code:
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg class="v5chart" width="960" height="500"></svg>
<style>
/*Rectangle bar class styling*/
.bar {
fill: #0080FF
}
.bar:hover {
fill: #003366
}
/*Text class styling*/
.text {
fill: white;
font-family: sans-serif
}
</style>
<script>
////VERTICAL BAR CHART WITH SVG AND NAMES
// Create data array of values to visualize
var dataArray = [{ "Player": "John Doe", "Points": 23 }, { "Player": "Jane Doe", "Points": 13 }, { "Player": "Mary Jane", "Points": 21 }, { "Player": "Debasis Das", "Points": 14 }, { "Player": "Nishant", "Points": 37 }, { "Player": "Mark", "Points": 15 }, { "Player": "Andrew", "Points": 18 }, { "Player": "Simon", "Points": 34 }, { "Player": "Lisa", "Points": 30 }, { "Player": "Marga", "Points": 20 }];
// Create variable for the SVG
var canvas = d3.select(".v5chart1").append("g").attr("transform", "translate(20,30)");
var canvasWidth = 500;
var maxValue = d3.max(dataArray, function (d) { return d.Points; });
var canvasHeight = maxValue*10;
var heightScale = d3.scaleLinear()
.domain([0, d3.max(dataArray, function (d) { return d.Points; })])
.range([canvasHeight, 0]); //use max value (37) * 10
var y_axis = d3.axisLeft()
.scale(heightScale);
var x = d3.scaleBand()
.rangeRound([0, canvasWidth], .1);
x.domain(dataArray.map(function (d) { return d.Player; }));
var x_Axis = d3.axisBottom(x);
// Select, append to SVG, and add attributes to rectangles for bar chart
canvas.selectAll("rect")
.data(dataArray)
.enter().append("rect")
.attr("class", "bar")
.attr("height", function (d, i) { return (d.Points * 10) })
.attr("width", canvasWidth/dataArray.length)
.attr("x", function (d, i) { return (i * (canvasWidth / dataArray.length)) })
.attr("y", function (d, i) { return canvasHeight - (d.Points * 10) });
// Select, append to SVG, and add attributes to text
canvas.selectAll("text")
.data(dataArray)
.enter().append("text")
.text(function (d) { return d.Points })
.attr("class", "text")
.attr("x", function (d, i) { return (i * (canvasWidth / dataArray.length)) + (canvasWidth / dataArray.length)/2 })
.attr("y", function (d, i) { return canvasHeight + 20 - (d.Points * 10) });
canvas.append("g")
.attr("transform", "translate(0,0)")
.call(y_axis);
canvas.append("g")
.attr("transform", "translate(0," + canvasHeight + ")")
.call(x_Axis)
.selectAll("text")
.attr("x",40)
.attr("transform", function (d) {
return "rotate(65)"
});
</script>
I already checked here: https://www.d3-graph-gallery.com/graph/custom_axis.html
You should have read properly the scaleBand example on the link that you provided:
scaleBand provides a convenient bandwidth() method to provide you with the width for each bar
the idea od axis in d3js is that you don't need to do calculations yourself, so in your case you can just pass the player name to the x function and it will do the coordinate calculations for you.
same applies to the y calculations, but I leave this for you to figure out, it should not be hard at all.
one more small thing about scaleBand, you were using rangeRound() method, which I am not familiar with, but if you use range() method combined with padding() as it is in the example you linked, then by adjusting the padding value you can control the width of the bar, without affecting the x axis. The higher value, the thinner will be the bar and more space would be between the bars.
////VERTICAL BAR CHART WITH SVG AND NAMES
// Create data array of values to visualize
var dataArray = [{ "Player": "John Doe", "Points": 23 }, { "Player": "Jane Doe", "Points": 13 }, { "Player": "Mary Jane", "Points": 21 }, { "Player": "Debasis Das", "Points": 14 }, { "Player": "Nishant", "Points": 37 }, { "Player": "Mark", "Points": 15 }, { "Player": "Andrew", "Points": 18 }, { "Player": "Simon", "Points": 34 }, { "Player": "Lisa", "Points": 30 }, { "Player": "Marga", "Points": 20 }];
// Create variable for the SVG
var canvas = d3.select(".v5chart").append("g").attr("transform", "translate(20,30)");
var canvasWidth = 500;
var maxValue = d3.max(dataArray, function (d) { return d.Points; });
var heightScale = d3.scaleLinear()
.domain([0, d3.max(dataArray, function (d) { return d.Points; })])
.range([maxValue * 10, 0]); //use max value (37) * 10
var y_axis = d3.axisLeft()
.scale(heightScale);
var x = d3.scaleBand()
.range([0, canvasWidth]).padding([0.1]);
x.domain(dataArray.map(function (d) { return d.Player; }));
var x_Axis = d3.axisBottom(x);
// Select, append to SVG, and add attributes to rectangles for bar chart
canvas.selectAll("rect")
.data(dataArray)
.enter().append("rect")
.attr("class", "bar")
.attr("height", function (d, i) { return (d.Points * 10) })
.attr("width", x.bandwidth())
.attr("x", function (d, i) { return x(d.Player); })
.attr("y", function (d, i) { return 370 - (d.Points * 10) });
// Select, append to SVG, and add attributes to text
canvas.selectAll("text")
.data(dataArray)
.enter().append("text")
.text(function (d) { return d.Points })
.attr("class", "text")
.attr("text-anchor", "middle")
.attr("x", function (d, i) { return x(d.Player)+x.bandwidth()/2; })
.attr("y", function (d, i) { return 390 - (d.Points * 10) });
canvas.append("g")
.attr("transform", "translate(0,0)")
.call(y_axis);
canvas.append("g")
.attr("transform", "translate(0,370)")
.call(x_Axis);
.bar {
fill: #0080FF
}
.bar:hover {
fill: #003366
}
/*Text class styling*/
.text {
fill: white;
font-family: sans-serif
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg class="v5chart" width="960" height="500"></svg>

d3 error displaying datetime on x-axis line chart

I am implementing a multi-line series chart using d3.js and I am getting an error pointing to my x-axis when trying to plot my dateTime from the data coming in. "Error: attribute d: Expected number, "MNaN,376.88020650…"."
Here is my function
var data = [{
"Brand": "Toyota",
"Count": 1800,
"Time": "2017-04-02 16"},
{
"Brand": "Toyota",
"Count": 1172,
"Time": "2017-04-02 17"},
{
"Brand": "Toyota",
"Count": 2000,
"Time": "2017-04-02 18"},
{
"Brand": "Honda",
"Count": 8765,
"Time": "2017-04-02 16"},
{
"Brand": "Honda",
"Count": 3445,
"Time": "2017-04-02 17"},
{
"Brand": "Honda",
"Count": 1232,
"Time": "2017-04-02 18"}
]
var dataGroup = d3.nest() //d3 method that groups data by Brand
.key(function(d) {return d.Brand;})
.entries(data);
console.log(JSON.stringify(dataGroup));
//var color = d3.scale.category10();
var vis = d3.select("#visualisation"),
WIDTH = 1000,
HEIGHT = 500,
MARGINS = {
top: 50,
right: 20,
bottom: 50,
left: 50
},
xScale = d3.scaleLinear().range([MARGINS.left, WIDTH - MARGINS.right]).domain([d3.min(data, function(d) { //set up x-axis based on data
return d.Time;
}), d3.max(data, function(d) {
return d.Time;
})]),
yScale = d3.scaleLinear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([d3.min(data, function(d) { //set up y-axis based on data
return d.Count;
}), d3.max(data, function(d) {
return d.Count;
})]),
xAxis = d3.axisBottom()
.scale(xScale),
yAxis = d3.axisLeft()
.scale(yScale)
vis.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
.call(xAxis);
vis.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(" + (MARGINS.left) + ",0)")
.call(yAxis);
var lineGen = d3.line()
.x(function(d) {
return xScale(d.Time);
})
.y(function(d) {
return yScale(d.Count);
})
.curve(d3.curveBasis);
dataGroup.forEach(function(d,i) { //iterate over the dataGroup and create line graph for each brand
vis.append('svg:path')
.attr('d', lineGen(d.values))
.attr('stroke', function(d,j) {
return "hsl(" + Math.random() * 360 + ",100%,50%)"; //random color for each brand line on graph
})
.attr('stroke-width', 2)
.attr('id', 'line_'+d.key)
.attr('fill', 'none');
lSpace = WIDTH/dataGroup.length; //define the legend space based on number of brands
vis.append("text")
.attr("x", (lSpace/2)+i*lSpace)
.attr("y", HEIGHT)
.style("fill", "black")
.attr("class","legend")
.on('click',function(){
var active = d.active ? false : true;
var opacity = active ? 0 : 1;
d3.select("#line_" + d.key).style("opacity", opacity);
d.active = active;
})
.text(d.key);
});
My dates are in yyyy-mm-dd HH format and what I am trying to accomplish is this for example:
"Time": "2017-04-02 16" converted to 'April 02' on the x axis and have the hour (HH) just displayed as a tool tip...etc
Here is a jsfiddle link https://jsfiddle.net/rsov2s2s/
Any help is appreciated.
In your data objects, Time is only a string. Thus, you`ll have to parse it into an actual date:
data.forEach(function(d){
d.Time = d3.timeParse("%Y-%m-%d %H")(d.Time)
});
In this function, d3.timeParse uses "%Y-%m-%d %H" as a specifier, which matches the structure of your strings.
After that, don't forget to change the xScale from scaleLinear to scaleTime.
Here is your code with those changes only:
var data = [{
"Brand": "Toyota",
"Count": 1800,
"Time": "2017-04-02 16"
}, {
"Brand": "Toyota",
"Count": 1172,
"Time": "2017-04-02 17"
}, {
"Brand": "Toyota",
"Count": 2000,
"Time": "2017-04-02 18"
}, {
"Brand": "Honda",
"Count": 8765,
"Time": "2017-04-02 16"
}, {
"Brand": "Honda",
"Count": 3445,
"Time": "2017-04-02 17"
}, {
"Brand": "Honda",
"Count": 1232,
"Time": "2017-04-02 18"
}];
data.forEach(function(d) {
d.Time = d3.timeParse("%Y-%m-%d %H")(d.Time)
});
var dataGroup = d3.nest() //d3 method that groups data by Brand
.key(function(d) {
return d.Brand;
})
.entries(data);
//var color = d3.scale.category10();
var vis = d3.select("#visualisation"),
WIDTH = 1000,
HEIGHT = 500,
MARGINS = {
top: 50,
right: 20,
bottom: 50,
left: 50
},
xScale = d3.scaleTime().range([MARGINS.left, WIDTH - MARGINS.right]).domain([d3.min(data, function(d) { //set up x-axis based on data
return d.Time;
}), d3.max(data, function(d) {
return d.Time;
})]),
yScale = d3.scaleLinear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([d3.min(data, function(d) { //set up y-axis based on data
return d.Count;
}), d3.max(data, function(d) {
return d.Count;
})]),
xAxis = d3.axisBottom()
.scale(xScale),
yAxis = d3.axisLeft()
.scale(yScale)
vis.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
.call(xAxis);
vis.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(" + (MARGINS.left) + ",0)")
.call(yAxis);
var lineGen = d3.line()
.x(function(d) {
return xScale(d.Time);
})
.y(function(d) {
return yScale(d.Count);
})
.curve(d3.curveBasis);
dataGroup.forEach(function(d, i) { //iterate over the dataGroup and create line graph for each brand
vis.append('svg:path')
.attr('d', lineGen(d.values))
.attr('stroke', function(d, j) {
return "hsl(" + Math.random() * 360 + ",100%,50%)"; //random color for each brand line on graph
})
.attr('stroke-width', 2)
.attr('id', 'line_' + d.key)
.attr('fill', 'none');
lSpace = WIDTH / dataGroup.length; //define the legend space based on number of brands
vis.append("text")
.attr("x", (lSpace / 2) + i * lSpace)
.attr("y", HEIGHT)
.style("fill", "black")
.attr("class", "legend")
.on('click', function() {
var active = d.active ? false : true;
var opacity = active ? 0 : 1;
d3.select("#line_" + d.key).style("opacity", opacity);
d.active = active;
})
.text(d.key);
});
.axis path {
fill: none;
stroke: #777;
shape-rendering: crispEdges;
}
.axis text {
font-family: Lato;
font-size: 13px;
}
.legend {
font-size: 14px;
font-weight: bold;
cursor: pointer;
<title>D3 Test</title>
<script src="https://d3js.org/d3.v4.js"></script>
<body>
<svg id="visualisation" width="1000" height="600"></svg>
<script src="InitChart.js"></script>
</body>

modifying d3.js donut chart to read from json array

How can I modify this example to read from a JSON array instead of CSV file? I will have a static JSON string that I would like to use as "data" rather than the CSV. Any pointers will be much appreciated.
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.csv("data.csv", type, function(error, data) {
if (error) throw error;
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.age); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.text(function(d) { return d.data.age; });
});
function type(d) {
d.population = +d.population;
return d;
}
Sample JSON data:
[
{
"age": "<5",
"population": 2704659
},
{
"age": "5-13",
"population": 4499890
},
{
"age": "14-17",
"population": 2159981
},
{
"age": "18-24",
"population": 3853788
},
{
"age": "25-44",
"population": 14106543
},
{
"age": "45-64",
"population": 8819342
},
{
"age": "≥65",
"population": 612463
}
]
This is an example from the following link. Original Example
Not a whole lot changes, really. Using the example you gave, just define a var called data and assign it your sample JSON data:
var data = [
{
"age": "<5",
"population": 2704659
},
{
"age": "5-13",
"population": 4499890
},
...etc
Then block out or remove the d3.csv() line at line # 53. And everything works just fine.
Here's a fiddle for you: https://jsfiddle.net/ej2s217f/
Just use d3.json
var data; // a global
d3.json("path/to/file.json", function(error, json) {
if (error) return console.warn(error);
data = json;
visualizeit();
});
Here is more on d3 requests.
Edit
If you don't want to load an external json here is a jsfiddle
All you have to do is drop the d3.json call and declare the var data = [...]
Basically, what remains is:
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) {
return d.population;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
data = [
{
"age": "<5",
"population": 2704659
},
{
"age": "5-13",
"population": 4499890
},
{
"age": "14-17",
"population": 2159981
},
{
"age": "18-24",
"population": 3853788
},
{
"age": "25-44",
"population": 14106543
},
{
"age": "45-64",
"population": 8819342
},
{
"age": "≥65",
"population": 612463
}
];
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function (d) {
return color(d.data.age);
});
g.append("text")
.attr("transform", function (d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.text(function (d) {
return d.data.age;
});
function type(d) {
d.population = +d.population;
return d;
}

switch d3 chart on selection of dropdown

Hi I'm implementing the D3.js chart in my module.I need that on the basis of my dropdown selection my chart type will update accordingly.
JsFiddle
I have sample.json file to retrieve data from json.
{
"sample2": [{
"time": 1387212120,
"open": 368,
"close": 275,
"high": 380,
"low": 158
}, {
"time": 1387212130,
"open": 330,
"close": 350,
"high": 389,
"low": 310
}, {
"time": 1387212140,
"open": 213,
"close": 253,
"high": 289,
"low": 213
}, {
"time": 1387212150,
"open": 180,
"close": 150,
"high": 189,
"low": 110
}, {
"time": 1387212160,
"open": 310,
"close": 350,
"high": 389,
"low": 310
}]
}
Line is the default chart selected in your demo. So you should kept that option selected by default in the drop down also (For now, I have put 'line' as the first option in the code snippet and will be selected by default). Also note that the data bonded to the pie chart paths was not correct. You should bind data as shown below and since pie chart need multiple paths to be drawn, you should use selectAll and enter method for this.
var container = canvas.selectAll(".arc")
.data(pie(sample2))
.enter().append("path")
instead of
canvas.append("path")
.datum(sample2);
And hide/show the x and y axes, when switching between pie chart and other charts.
// loading sample.json
var sample2 = {
"sample2": [{
"time": 1387212120,
"open": 368,
"close": 275,
"high": 380,
"low": 158
}, {
"time": 1387212130,
"open": 330,
"close": 350,
"high": 389,
"low": 310
}, {
"time": 1387212140,
"open": 213,
"close": 253,
"high": 289,
"low": 213
}, {
"time": 1387212150,
"open": 180,
"close": 150,
"high": 189,
"low": 110
}, {
"time": 1387212160,
"open": 310,
"close": 350,
"high": 389,
"low": 310
}]
};
sample2 = sample2.sample2
// date manipulation to format UTC to js Date obj
sample2.forEach(function(d) {
d.time = new Date(d.time * 1000);
});
// helpers and constants
var margin = {
"top": 50,
"right": 100,
"bottom": 56,
"left": 50
};
var width = 930 - margin.right - margin.left;
var height = 582 - margin.top - margin.bottom;
var radius = Math.min(width, height) / 2;
var timeFormat = d3.time.format("%c");
var X = width / sample2.length * 0.25;
// find data range
var xDomain = d3.extent(sample2, function(d, i) {
return d.time;
});
var yMin = d3.min(sample2, function(d) {
return Math.min(d.low);
});
var yMax = d3.max(sample2, function(d) {
return Math.max(d.high);
});
// scales, add 10pc padding to x-domain
var xScale = d3.time.scale()
.domain(xDomain);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 20);
xScale.domain([-0.1, 1.1].map(xScale.invert))
.range([margin.left, width - margin.right]);
var yScale = d3.scale.linear()
.domain([yMin, yMax])
.range([height - margin.top, margin.bottom]);
// set up axes
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(10)
.tickPadding(10);
// .tickFormat(timeFormat)
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("right")
.tickValues(yScale.domain());
// set up chart types
var area = d3.svg.area()
.x(function(d) {
return xScale(d.time);
})
.y0(height - margin.bottom)
.y1(function(d) {
return yScale(d.close);
});
var line = d3.svg.line().interpolate("monotone")
.x(function(d) {
return xScale(d.time);
})
.y(function(d) {
return yScale(d.close);
});
var pie = d3.layout.pie()
.value(function(d) {
return d.time;
})
.sort(null);
// create svg container and offset
var canvas = d3.select("body").append("svg")
.attr({
"width": width,
"height": height
})
.append("g")
.attr("transform", "translate(" + margin.top / 2 + "," + margin.left / 2 + ")");
var color = d3.scale.category20();
// gridsk
canvas.append("svg:rect")
.attr({
"width": width - margin.right - margin.left,
"height": height - margin.bottom - margin.top,
"class": "plot",
"transform": "translate(" + margin.top + "," + margin.bottom + ")"
});
// chart options by type
var chartDraw = {
candle: function() {
canvas.selectAll("line.candle")
.data(sample2)
.enter()
.append("svg:line")
.attr({
"class": "candle alt-view",
"x1": function(d, i) {
return xScale(d.time) - X * 0.5;
},
"x2": function(d, i) {
return xScale(d.time) - X * 0.5;
},
"y1": function(d, i) {
return yScale(d.high);
},
"y2": function(d, i) {
return yScale(d.low);
},
"stroke": "black"
});
canvas.selectAll("rect.candle")
.data(sample2)
.enter()
.append("svg:rect")
.attr({
"class": "candle alt-view",
"width": function(d) {
return X
},
"x": function(d, i) {
return xScale(d.time) - X;
},
"y": function(d, i) {
return yScale(Math.max(d.open, d.close));
},
"height": function(d, i) {
return yScale(Math.min(d.open, d.close)) - yScale(Math.max(d.open, d.close));
},
"fill": function(d) {
return d.open > d.close ? "#dc432c" : "#0CD1AA"
},
"stroke": "gray"
});
},
line: function() {
canvas.append("path")
.datum(sample2)
.attr("class", "line alt-view")
.attr("d", line);
},
pie: function() {
var container = canvas.selectAll(".arc")
.data(pie(sample2))
.enter().append("path")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.attr("class", "pie alt-view")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc)
.each(function(d) {
this._current = d;
});
},
area: function() {
canvas.append("path")
.datum(sample2)
.attr("class", "area alt-view")
.attr("d", area);
}
};
// draw axes
canvas.append('g').classed("axis", true).call(xAxis)
.attr('transform', 'translate(0, ' + (height - margin.bottom) + ')');
canvas.append('g').classed("axis", true).call(yAxis)
.attr('transform', 'translate(' + (width - margin.right) + ', 0)');
// drop down menu
var chartOptions = ["line", "candle", "area", "pie"];
var dropdown = d3.select("body").append("foreignObject")
.attr({
"height": 100,
"width": 300,
"transform": "translate(" + margin.left * 1.3 + "," + margin.top * 0.7 + ")"
})
.append("xhtml:select")
.on("change", function() {
d3.selectAll(".alt-view").remove();
selected = this.value;
if (selected == "line") {
canvas.selectAll(".axis").style("display", "block");
canvas.select(".plot").style("display", "block");
chartDraw.line();
} else if (selected == "area") {
canvas.selectAll(".axis").style("display", "block");
canvas.select(".plot").style("display", "block");
chartDraw.area();
} else if (selected == "candle") {
canvas.selectAll(".axis").style("display", "block");
canvas.select(".plot").style("display", "block");
chartDraw.candle();
} else if (selected == "pie") {
canvas.selectAll(".axis").style("display", "none");
canvas.select(".plot").style("display", "none");
chartDraw.pie();
}
})
.attr("id", "drop-down")
.selectAll("option")
.data(chartOptions)
.enter()
.append("option")
.text(function(d) {
return d;
})
.attr("value", function(d) {
return d;
});
// default chart
chartDraw.line();
.line {
fill: none;
stroke: #0CD1AA;
stroke-width: 3px;
}
.area {
fill: #0cd1aa;
stroke: #dc432c;
stroke-width 0.5;
}
.plot {
fill: lightblue;
opacity: 0.5;
}
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="inlet.js"></script>
<link src="style.css" />
<body></body>

Categories