Related
I'm struggling with this problem, I'm trying to zoom my chart, specifically the lines that i draw dynamically.
Here my fiddle: https://jsfiddle.net/w3j89Lf3/
<link href="http://getbootstrap.com/examples/justified-nav/justified-nav.css" rel="stylesheet">
<style>
.axis path {
fill: none;
stroke: #777;
shape-rendering: crispEdges;
}
.axis text {
font-family: Lato;
font-size: 13px;
}
.legend {
font-size: 14px;
font-weight: bold;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
</style>
My code works well with x and y axes, but doesn't work for my lines as you can see in my function "zoomed".
<div class="container">
<div class="jumbotron">
<svg id="visualisation" width="1000" height="500"></svg>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
function InitChart() {
var data = [{
"Client": "ABC",
"sale": "202",
"year": "2000"
}, {
"Client": "ABC",
"sale": "215",
"year": "2002"
}, {
"Client": "ABC",
"sale": "179",
"year": "2004"
}, {
"Client": "ABC",
"sale": "199",
"year": "2006"
}, {
"Client": "ABC",
"sale": "134",
"year": "2008"
}, {
"Client": "ABC",
"sale": "176",
"year": "2010"
}, {
"Client": "XYZ",
"sale": "100",
"year": "2000"
}, {
"Client": "XYZ",
"sale": "215",
"year": "2002"
}, {
"Client": "XYZ",
"sale": "179",
"year": "2004"
}, {
"Client": "XYZ",
"sale": "199",
"year": "2006"
}, {
"Client": "XYZ",
"sale": "134",
"year": "2008"
}, {
"Client": "XYZ",
"sale": "176",
"year": "2013"
}];
var dataGroup = d3.nest()
.key(function(d) {
return d.Client;
})
.entries(data);
var vis = d3.select("#visualisation"),
WIDTH = 1000,
HEIGHT = 500,
MARGINS = {
top: 50,
right: 20,
bottom: 50,
left: 50
};
//Aggiungi ASSI con scala
xScale = d3.scale.linear().range([MARGINS.left, WIDTH - MARGINS.right]).domain([d3.min(data, function(d) {
return d.year;
}), d3.max(data, function(d) {
return d.year;
})]),
yScale = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([d3.min(data, function(d) {
return d.sale;
}), d3.max(data, function(d) {
return d.sale;
})]),
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);
vis.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(" + (MARGINS.left) + ",0)")
.call(yAxis);
//Aggiungi GRID
function make_x_axis() {
return d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(10)
}
function make_y_axis() {
return d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10)
}
vis.append("svg:g")
.attr("class", "grid")
.attr("transform", "translate(0," + HEIGHT + ")")
.call( make_x_axis()
.tickSize(-HEIGHT, 0, 0)
.tickFormat("")
)
vis.append("svg:g")
.attr("class", "grid")
.call( make_y_axis()
.tickSize(-WIDTH, 0, 0)
.tickFormat("")
)
var lineGen = d3.svg.line()
.x(function(d) {
return xScale(d.year);
})
.y(function(d) {
return yScale(d.sale);
})
.interpolate("basis"); //linear
lSpace = WIDTH/dataGroup.length;
dataGroup.forEach(function(d, i) {
color = "hsl(" + Math.random() * 360 + ",100%,50%)";
vis.append('svg:path')
.attr('d', lineGen(d.values))
.attr('stroke', color)
.attr('stroke-width', 2)
.attr('id', 'line_'+d.key)
.attr('fill', 'none');
vis.append("text")
.attr("x", (lSpace / 2) + i * lSpace)
.attr("y", HEIGHT)
//.style("stroke", "black")
.style("fill", color)
.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);
});
function zoomed() {
vis.select(".x.axis").call(xAxis);
vis.select(".y.axis").call(yAxis);
vis.selectAll('path.line').attr("d", function(d) {return line(d.values)}); **-> HERE I WOULD ZOOM/TRANSFORM**
}
var zoom = d3.behavior.zoom()
.x(xScale)
.y(yScale)
.scaleExtent([1, 10])
.on("zoom", zoomed);
vis.call(zoom)
}
InitChart();
</script>
</div>
</div>
There are two things that should be changed to make it work.
First in zoom function you end up with an empty selection because the paths do not have line class. Add line class to paths while generating them to make the .selectAll('path.line') work.
Second, you did not bind data to the paths so you cannot use it later in the zoom function. If you pass an anonymous function as the second parameter of .attr then the first argument of this function (d, in your case) will be the data bound to the selection. To make this line work:
vis.selectAll('path.line').attr("d", function(d) {return line(d.values)});
you have to add data to the paths. One way to do it is like this:
vis.append('svg:path').datum(d)
However, there is a better way to bind data and enter new elements than the forEach loop you use. I recommend this very helpful tutorial by Scott Murray that explains d3 data bind.
Here is the updated jsfiddle.
I am using the example chart from http://bl.ocks.org/mbostock/3885304 and rather than use the tsv example, I wish to use json from api call.
The chart does display, in the sense that it gives a large blob of what looks like a single bar.
The JSON returned from the api.php call looks like this
[{"id":"2","name":"wombat","total":"98000","record_date":"2016-01-21 00:00:00"},{"id":"5","name":"wombat","total":"96000","record_date":"2016-02-21 00:00:00"},{"id":"8","name":"wombat","total":"93000","record_date":"2016-03-21 00:00:00"},{"id":"11","name":"wombat","total":"91000","record_date":"2016-04-21 00:00:00"},{"id":"14","name":"wombat","total":"92000","record_date":"2016-05-21 00:00:00"},{"id":"17","name":"wombat","total":"83000","record_date":"2016-06-21 00:00:00"},{"id":"20","name":"wombat","total":"81000","record_date":"2016-07-21 00:00:00"}]
Plunker
Thanks
Kevin
Your json data array has the record_date property instead of event_time. But your code refers to event_time property. Try replacing them with the record_date.
I could not find no other errors in your code.
Here is the working Code Snippet:
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10, "");
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 = [{
"id": "2",
"name": "wombat",
"total": "98000",
"record_date": "2016-01-21 00:00:00"
}, {
"id": "5",
"name": "wombat",
"total": "96000",
"record_date": "2016-02-21 00:00:00"
}, {
"id": "8",
"name": "wombat",
"total": "93000",
"record_date": "2016-03-21 00:00:00"
}, {
"id": "11",
"name": "wombat",
"total": "91000",
"record_date": "2016-04-21 00:00:00"
}, {
"id": "14",
"name": "wombat",
"total": "92000",
"record_date": "2016-05-21 00:00:00"
}, {
"id": "17",
"name": "wombat",
"total": "83000",
"record_date": "2016-06-21 00:00:00"
}, {
"id": "20",
"name": "wombat",
"total": "81000",
"record_date": "2016-07-21 00:00:00"
}];
var k = [];
data.forEach(function(d) {
d.record_date = d.record_date;
d.total = +d.total;
k.push(d.record_date)
});
x.domain(data.map(function(d) {
return d.record_date;
}));
y.domain([0, d3.max(data, function(d) {
return d.total;
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Count");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.record_date);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.total);
})
.attr("height", function(d) {
return height - y(d.total);
});
function type(d) {
d.total = +d.total;
return d;
}
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
My goal is making a multiple line of line drawing. It is all working fine other than adding the circles on the curves.
Below is the code snippets. HTML and CSS also added below. Can anyone please help me to fix this?
window.onload = function () {
var datas = [
{"date":20111001,"New York" : 63.4, "San Franscisco" : 62.7, "Austin" : 72.2 },
{"date":20111002,"New York" : 58.0, "San Franscisco" : 59.9, "Austin" : 67.7 },
{"date":20111003,"New York" : 53.3, "San Franscisco" : 59.1, "Austin" : 69.4 },
{"date":20111004,"New York" : 55.7, "San Franscisco" : 58.8, "Austin" : 68.0 },
{"date":20111005,"New York" : 64.2, "San Franscisco" : 58.7, "Austin" : 72.4 },
{"date":20111006,"New York" : 58.8, "San Franscisco" : 57.0, "Austin" : 77.0 },
{"date":20111007,"New York" : 57.9, "San Franscisco" : 56.7, "Austin" : 82.3 },
{"date":20111008,"New York" : 61.8, "San Franscisco" : 56.8, "Austin" : 78.9 },
{"date":20111009,"New York" : 69.3, "San Franscisco" : 56.7, "Austin" : 68.8 },
{"date":20111010,"New York" : 71.2, "San Franscisco" : 60.1, "Austin" : 68.7 }
]
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var color = d3.scale.ordinal()
.domain(["New York", "San Francisco", "Austin"])
.range(["#FF0000", "#009933" , "#0000FF"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.date); })
.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 + ")");
color.domain(d3.keys(datas[0]).filter(function(key) { return key !== "date"; }));
datas.forEach(function(d) {
d.date = parseDate(d.date + "");
});
var cities = color.domain().map(function(name) {
return {
name: name,
values: datas.map(function(d) {
return {date: d.date, temperature: +d[name]};
})
};
});
x.domain(d3.extent(datas, function(d) { return d.date; }));
y.domain([
d3.min(cities, function(c) { return d3.min(c.values, function(v) { return v.temperature; }); }),
d3.max(cities, function(c) { return d3.max(c.values, function(v) { return v.temperature; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
// .append("text")
// .attr("transform", "rotate(-90)")
// .attr("y", 6)
// .attr("dy", ".71em")
// .style("text-anchor", "end")
// .text("Temperature (ºF)");
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
var path = city.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
var totalLength = [path[0][0].getTotalLength()];
path
.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.ease("linear")
.attr("stroke-dashoffset", 0);
city.selectAll("circle.line")
.data(cities)
.enter().append("svg:circle")
.attr("class", "line")
.attr("stroke-width", 2)
.attr("stroke", "green")
.attr("fill", "transparent")
.attr("cx", function(d,i){ return line(d.values) })
.attr("cy", function(d,i){ return line(d.values) })
.attr("r", 3.5);
// city.append("text")
// .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
// .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; })
// .attr("x", 3)
// .attr("dy", ".35em")
// .text(function(d) { return d.name; });
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Try this way.
city.selectAll("circle")
.data(function(d) {
return d.values
})
.enter()
.append("circle")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.temperature)
})
.attr("stroke-width", 2)
.attr("stroke", "green")
.attr("fill", "transparent");
Note: I have commented the line interpolation, since it is not possible to find the exact point positions in that case. Reference: points on line in d3.js
window.onload = function() {
var datas = [{
"date": 20111001,
"New York": 63.4,
"San Franscisco": 62.7,
"Austin": 72.2
}, {
"date": 20111002,
"New York": 58.0,
"San Franscisco": 59.9,
"Austin": 67.7
}, {
"date": 20111003,
"New York": 53.3,
"San Franscisco": 59.1,
"Austin": 69.4
}, {
"date": 20111004,
"New York": 55.7,
"San Franscisco": 58.8,
"Austin": 68.0
}, {
"date": 20111005,
"New York": 64.2,
"San Franscisco": 58.7,
"Austin": 72.4
}, {
"date": 20111006,
"New York": 58.8,
"San Franscisco": 57.0,
"Austin": 77.0
}, {
"date": 20111007,
"New York": 57.9,
"San Franscisco": 56.7,
"Austin": 82.3
}, {
"date": 20111008,
"New York": 61.8,
"San Franscisco": 56.8,
"Austin": 78.9
}, {
"date": 20111009,
"New York": 69.3,
"San Franscisco": 56.7,
"Austin": 68.8
}, {
"date": 20111010,
"New York": 71.2,
"San Franscisco": 60.1,
"Austin": 68.7
}
]
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var color = d3.scale.ordinal()
.domain(["New York", "San Francisco", "Austin"])
.range(["#FF0000", "#009933", "#0000FF"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
// .interpolate("basis")
.x(function(d) {
return x(d.date);
})
.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 + ")");
color.domain(d3.keys(datas[0]).filter(function(key) {
return key !== "date";
}));
datas.forEach(function(d) {
d.date = parseDate(d.date + "");
});
var cities = color.domain().map(function(name) {
return {
name: name,
values: datas.map(function(d) {
return {
date: d.date,
temperature: +d[name]
};
})
};
});
x.domain(d3.extent(datas, function(d) {
return d.date;
}));
y.domain([
d3.min(cities, function(c) {
return d3.min(c.values, function(v) {
return v.temperature;
});
}),
d3.max(cities, function(c) {
return d3.max(c.values, function(v) {
return v.temperature;
});
})
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
// .append("text")
// .attr("transform", "rotate(-90)")
// .attr("y", 6)
// .attr("dy", ".71em")
// .style("text-anchor", "end")
// .text("Temperature (ºF)");
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
var path = city.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
});
var totalLength = [path[0][0].getTotalLength()];
path
.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.ease("linear")
.attr("stroke-dashoffset", 0);
city.selectAll("circle")
.data(function(d) {
return d.values
})
.enter()
.append("circle")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.temperature)
})
.attr("r", 3.5)
.attr("fill", "transparent")
.attr("stroke", "green")
.attr("stroke-width", 0)
.transition()
.duration(2000)
.attr("stroke-width", 2)
// city.append("text")
// .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
// .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; })
// .attr("x", 3)
// .attr("dy", ".35em")
// .text(function(d) { return d.name; });
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I want to create a Stacked bar chart like http://bl.ocks.org/mbostock/3886208 . But I don't want to use CSV file.
How can I create Stacked chart using array or JSON data?
In csv we are using like this :
State,Post,Comment
AL,310504,552339
AK,52083,85640
How can I define data in array or json like
var data = []
do it like this
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
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 = [
{
"State": "AL",
"Under 5 Years": 10,
"5 to 13 Years": 20,
"14 to 17 Years": 30,
"18 to 24 Years": 40,
"25 to 44 Years": 50,
"45 to 64 Years": 60,
"65 Years and Over": 70
},{
"State": "AK",
"Under 5 Years": 15,
"5 to 13 Years": 25,
"14 to 17 Years": 35,
"18 to 24 Years": 45,
"25 to 44 Years": 55,
"45 to 64 Years": 65,
"65 Years and Over": 75
}];
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { return d.State; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
</script>
I know late for replying to this one. I modified #heshjse's code for D3 version 4. When I tried the above code with d3 v3, it's working fine. But we had limitation to use version 4, I was getting problem bcoz of some changes in d3 v4. So adding the code which worked for me. I hope it helps.
This should work fine for Json format in d3 v4.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
</head>
<body>
<div id="Dash"></div>
</body>
</html>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
drawStackChart();
});
//Draw Stack Chart
var marginStackChart = { top: 20, right: 20, bottom: 30, left: 40 },
widthStackChart = 500 - marginStackChart.left - marginStackChart.right,
heightStackChart = 300 - marginStackChart.top - marginStackChart.bottom;
var xStackChart = d3.scaleBand()
.range([0, widthStackChart])
.padding(0.1);
var yStackChart = d3.scaleLinear()
.range([heightStackChart, 0]);
var colorStackChart = d3.scaleOrdinal(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"])
var canvasStackChart = d3.select("#Dash").append("svg")
.attr("width", widthStackChart + marginStackChart.left + marginStackChart.right)
.attr("height", heightStackChart + marginStackChart.top + marginStackChart.bottom)
.append("g")
.attr("transform", "translate(" + marginStackChart.left + "," + marginStackChart.top + ")");
function drawStackChart() {
var data = [
{
"Year": "2012",
"Category1": "20",
"Category2": "5",
"Category3": "5",
"Category4": "5",
"Category5": "5",
"Category6": "5",
"Category7": "5",
"Category8": "5",
"Category9": "5"
},
{
"Year": "2013",
"Category1": "30",
"Category2": "10",
"Category3": "10",
"Category4": "10",
"Category5": "10",
"Category6": "10",
"Category7": "10",
"Category8": "10",
"Category9": "10"
},
{
"Year": "2014",
"Category1": "35",
"Category2": "15",
"Category3": "15",
"Category4": "15",
"Category5": "15",
"Category6": "15",
"Category7": "15",
"Category8": "15",
"Category9": "15"
},
{
"Year": "2015",
"Category1": "60",
"Category2": "20",
"Category3": "20",
"Category4": "20",
"Category5": "20",
"Category6": "20",
"Category7": "20",
"Category8": "20",
"Category9": "20"
},
{
"Year": "2016",
"Category1": "70",
"Category2": "40",
"Category3": "40",
"Category4": "40",
"Category5": "40",
"Category6": "40",
"Category7": "40",
"Category8": "40",
"Category9": "40"
}
];
colorStackChart.domain(d3.keys(data[0]).filter(function (key) { return key !== "Year"; }));
data.forEach(function (d) {
var y0 = 0;
d.ages = colorStackChart.domain().map(function (name) { return { name: name, y0: y0, y1: y0 += +d[name] }; });
d.total = d.ages[d.ages.length - 1].y1;
});
data.sort(function (a, b) { return b.total - a.total; });
xStackChart.domain(data.map(function (d) { return d.Year; }));
yStackChart.domain([0, d3.max(data, function (d) { return d.total; })]);
canvasStackChart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + heightStackChart + ")")
.call(d3.axisBottom(xStackChart));
canvasStackChart.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yStackChart))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("No Of Buildings");
var state = canvasStackChart.selectAll(".Year")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function (d) { return "translate(" + xStackChart(d.Year) + ",0)"; });
state.selectAll("rect")
.data(function (d) { return d.ages; })
.enter().append("rect")
.attr("width", xStackChart.bandwidth())
.attr("y", function (d) { return yStackChart(d.y1); })
.attr("height", function (d) { return yStackChart(d.y0) - yStackChart(d.y1); })
.style("fill", function (d) { return colorStackChart(d.name); });
var legend = canvasStackChart.selectAll(".legend")
.data(colorStackChart.domain().slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", widthStackChart - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", colorStackChart);
legend.append("text")
.attr("x", widthStackChart - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function (d) { return d; });
}
</script>
If you have an array, data, you can use that just like the parameter data in the csv function in the example you linked. The code within that function will work as expected, provided that your data is in the same format.
If you can set breakpoints with your browser, you can have a look at what that format is fairly easily, set one just inside the csv function call in the js and look at the data variable.
I make a scatterplot using d3.js where the points are scattered according to their name and an accompanying number. The x axis displays the group_1 once per group.
The data that is plotted would look like this:
name, group_1, group_2, number
"a", "A", "1", 0.5
"b", "A", "1", 10.0
"c", "A", "1", 5.0
"d", "A", "2", 1.0
"e", "A", "2", 3.0
"f", "A", "2", 10.0
"g", "B", "1", 3.0
Starting point.
The current code (based on this SO answer/example on bl.ocks.org) ...
var margin = {
top: 10,
right: 10,
bottom: 30,
left: 30
},
width = 500 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
offset = 7;
var chartData = [{
name: "a",
group_1: "A",
group_2: "1",
number: 0.5
}, {
name: "b",
group_1: "A",
group_2: "1",
number: 10
}, {
name: "c",
group_1: "A",
group_2: "1",
number: 5.0
}, {
name: "d",
group_1: "A",
group_2: "2",
number: 1.0
}, {
name: "e",
group_1: "A",
group_2: "2",
number: 3.0
}, {
name: "f",
group_1: "A",
group_2: "2",
number: 10.0
}, {
name: "g",
group_1: "B",
group_2: "1",
number: 3.0
}];
var x = d3.scale.ordinal()
.domain(chartData.map(function(d) {
return d.name;
}))
.rangePoints([0, width]);
var chartGroups = [];
chartData.forEach(function(d, i, array) {
if (i === 0) d.first = true;
else if (d.group_1 !== array[i - 1].group_1) d.first = true;
if (i === array.length - 1) d.last = true;
else if (d.group_1 !== array[i + 1].group_1) d.last = true;
if (d.first) chartGroups.push({
group: d.group_1,
start: i === 0 ? x(d.name) : ((x(d.name) + x(array[i - 1].name)) / 2)
});
if (d.last) chartGroups[chartGroups.length - 1].end = i === array.length - 1 ? x(d.name) : ((x(d.name) + x(array[i + 1].name)) / 2);
});
var y = d3.scale.linear()
.domain([0, d3.max(chartData, function(d) {
return d.number;
})])
.range([height, 0]),
xAxis = d3.svg.axis()
.scale(x)
.tickValues([])
.outerTickSize(offset)
.orient("bottom"),
yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
svg.selectAll("circle.point")
.data(chartData)
.enter().append("circle")
.attr({
"class": "point",
cx: function(d) {
return x(d.name);
},
cy: function(d) {
return y(d.number);
},
r: 5
});
var groups = svg.selectAll("g.chartGroup")
.data(chartGroups)
.enter().append("g")
.attr("class", "chartGroup")
.attr("transform", "translate(" + 0 + "," + (height + offset) + ")");
groups.append("text")
.attr({
x: function(d) {
return (d.start + d.end) / 2;
},
dy: "1em",
"text-anchor": "middle"
})
.text(function(d) {
return d.group;
});
groups.append("path")
.attr("d", function(d) {
var t = d3.select(this.parentNode).select("text").node().getBBox(),
ttop = [t.x + t.width / 2, t.y];
console.log(d, t, ttop);
return "M" + d.start + ",0" + "V" + -offset;
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
text {
font: 12px sans-serif;
}
.axis path,
.axis line,
g.chartGroup path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
circle.point {
fill: steelblue;
stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
... generates this plot:
What I want to achieve.
1. Zoom Level:
I want zoom in to only display all elements of A (from group_1) when clicking on the label A on the x axis. Besides the zooming, also the labeling of the x axis should change to incorporate data from group_2.
2. Zoom Level:
On the 2. zoom level, this should be repeated. By clicking on 1 (from group_2), I want a zoom to all elements of this group. The x axis should change to incorporate data from name
What I tried.
I tried adding basic zooming function (from this example on bl.ocks.org) through these code parts, but even this didn't work out:
var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([1, 10])
.on("zoom", zoomed);
var svg = d3.select("body")
// ...
.call(zoom);
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
}
The array chartGroups, which is based on group_1 is an array of calculated start and end coordinates for displaying the groups on the x axis. But I can't figure out, how to use them for zooming.
var margin = {
top: 10,
right: 10,
bottom: 30,
left: 30
},
width = 500 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
offset = 7;
var chartData = [{
name: "a",
group_1: "A",
group_2: "1",
number: 0.5
}, {
name: "b",
group_1: "A",
group_2: "1",
number: 10
}, {
name: "c",
group_1: "A",
group_2: "1",
number: 5.0
}, {
name: "d",
group_1: "A",
group_2: "2",
number: 1.0
}, {
name: "e",
group_1: "A",
group_2: "2",
number: 3.0
}, {
name: "f",
group_1: "A",
group_2: "2",
number: 10.0
}, {
name: "g",
group_1: "B",
group_2: "1",
number: 3.0
}];
var x = d3.scale.ordinal()
.domain(chartData.map(function(d) {
return d.name;
}))
.rangePoints([0, width]);
var chartGroups = [];
chartData.forEach(function(d, i, array) {
if (i === 0) d.first = true;
else if (d.group_1 !== array[i - 1].group_1) d.first = true;
if (i === array.length - 1) d.last = true;
else if (d.group_1 !== array[i + 1].group_1) d.last = true;
if (d.first) chartGroups.push({
group: d.group_1,
start: i === 0 ? x(d.name) : ((x(d.name) + x(array[i - 1].name)) / 2)
});
if (d.last) chartGroups[chartGroups.length - 1].end = i === array.length - 1 ? x(d.name) : ((x(d.name) + x(array[i + 1].name)) / 2);
});
var y = d3.scale.linear()
.domain([0, d3.max(chartData, function(d) {
return d.number;
})])
.range([height, 0]),
xAxis = d3.svg.axis()
.scale(x)
.tickValues([])
.outerTickSize(offset)
.orient("bottom"),
yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([1, 10])
.on("zoom", zoomed);
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(zoom);
svg.selectAll("circle.point")
.data(chartData)
.enter().append("circle")
.attr({
"class": "point",
cx: function(d) {
return x(d.name);
},
cy: function(d) {
return y(d.number);
},
r: 5
});
var groups = svg.selectAll("g.chartGroup")
.data(chartGroups)
.enter().append("g")
.attr("class", "chartGroup")
.attr("transform", "translate(" + 0 + "," + (height + offset) + ")");
groups.append("text")
.attr({
x: function(d) {
return (d.start + d.end) / 2;
},
dy: "1em",
"text-anchor": "middle"
})
.text(function(d) {
return d.group;
});
groups.append("path")
.attr("d", function(d) {
var t = d3.select(this.parentNode).select("text").node().getBBox(),
ttop = [t.x + t.width / 2, t.y];
console.log(d, t, ttop);
return "M" + d.start + ",0" + "V" + -offset;
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
}
text {
font: 12px sans-serif;
}
.axis path,
.axis line,
g.chartGroup path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
circle.point {
fill: steelblue;
stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Question.
How can I zoom to specficic areas just by clicking on a text element?
Edit.
As suggested in the comments, I tried to adjust the domain and redraw the circles. I did this the following way:
function zoom_update(group) {
//adjust domain
x.domain(chartData.map(function (d) {
if (d.group_1 == group) {
return d.name;
}
}));
console.log(x.domain());
svg.selectAll(".xaxis")
.transition(750)
.call(xAxis);
// update circles
svg.selectAll("circle")
.transition(750)
.attr({
cx: function (d) {return x(d.name);}
});
}
var margin = {
top: 10,
right: 10,
bottom: 30,
left: 30
},
width = 500 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
offset = 7;
var chartData = [{
name: "a",
group_1: "A",
group_2: "1",
number: 0.5
}, {
name: "b",
group_1: "A",
group_2: "1",
number: 10
}, {
name: "c",
group_1: "A",
group_2: "1",
number: 5.0
}, {
name: "d",
group_1: "A",
group_2: "2",
number: 1.0
}, {
name: "e",
group_1: "A",
group_2: "2",
number: 3.0
}, {
name: "f",
group_1: "A",
group_2: "2",
number: 10.0
}, {
name: "g",
group_1: "B",
group_2: "1",
number: 3.0
}];
var x = d3.scale.ordinal()
.rangePoints([0, width]);
x.domain(chartData.map(function(d) {
return d.name;
}));
var chartGroups = [];
chartData.forEach(function(d, i, array) {
if (i === 0) d.first = true;
else if (d.group_1 !== array[i - 1].group_1) d.first = true;
if (i === array.length - 1) d.last = true;
else if (d.group_1 !== array[i + 1].group_1) d.last = true;
if (d.first) chartGroups.push({
group: d.group_1,
start: i === 0 ? x(d.name) : ((x(d.name) + x(array[i - 1].name)) / 2)
});
if (d.last) chartGroups[chartGroups.length - 1].end = i === array.length - 1 ? x(d.name) : ((x(d.name) + x(array[i + 1].name)) / 2);
});
var y = d3.scale.linear()
.domain([0, d3.max(chartData, function(d) {
return d.number;
})])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickValues([])
.outerTickSize(offset)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
svg.selectAll("circle.point")
.data(chartData)
.enter().append("circle")
.attr({
"class": "point",
cx: function(d) {
return x(d.name);
},
cy: function(d) {
return y(d.number);
},
r: 5
});
var groups = svg.selectAll("g.chartGroup")
.data(chartGroups)
.enter().append("g")
.attr("class", "chartGroup")
.attr("transform", "translate(" + 0 + "," + (height + offset) + ")");
groups.append("text")
.attr({
x: function(d) {
return (d.start + d.end) / 2;
},
dy: "1em",
"text-anchor": "middle"
})
.on("click", function(d) {
zoom_update(d.group);
})
.text(function(d) {
return d.group;
});
groups.append("path")
.attr("d", function(d) {
var t = d3.select(this.parentNode).select("text").node().getBBox(),
ttop = [t.x + t.width / 2, t.y];
console.log(d, t, ttop);
return "M" + d.start + ",0" + "V" + -offset;
});
svg.append("g")
.attr("class", "xaxis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "yaxis")
.call(yAxis);
function zoom_update(group) {
x.domain(chartData.map(function(d) {
if (d.group_1 == group) {
return d.name;
}
}));
alert(x.domain());
svg.selectAll(".xaxis")
.transition(750)
.call(xAxis);
// update circles
svg.selectAll("circle")
.transition(750)
.attr({
cx: function(d) {
return x(d.name);
}
});
}
text {
font: 12px sans-serif;
}
.xaxis path,
.xaxis line,
.yaxis path,
.yaxis line,
.axis path,
.axis line,
g.chartGroup path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
circle.point {
fill: steelblue;
stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
The log shows that the domain is set depending on which label is clicked. In addition, the circles that are outside the new domain are moved to x=0, but the remaining circles are not spread over the whole domain.