Set arbitrary center in streamgraph offset - d3.js

I have data in a streamgraph stack layout and the desired aesthetic I'm after is to assign an arbitrary series as the center line(s). Series above those lines (as determined by their index in the data) will stack on top and series below that line will stack below.
Here's a jsFidde.
In this example, I'd like the MS and the RC series to be single horizontal lines with the other groups stacked above and below them, respectively. (As opposed to the data index, I could also set the middle series based on some data attribute, in this example, oldest date would make sense.)
I think the solution would require passing my own offset function but I'm having a hard time figuring out how the built-in ones do what they do.
HTML
<div class="chart">
JS
// Adapted from https://gist.github.com/WillTurman/4631136
var data = [
{"key":"DJ","value":0,"date":"1/8/13"},
{"key":"DJ","value":0,"date":"1/9/13"},
{"key":"DJ","value":0,"date":"1/10/13"},
{"key":"DJ","value":1,"date":"1/11/13"},
{"key":"DJ","value":1,"date":"1/12/13"},
{"key":"MS","value":0,"date":"1/8/13"},
{"key":"MS","value":1,"date":"1/9/13"},
{"key":"MS","value":1,"date":"1/10/13"},
{"key":"MS","value":1,"date":"1/11/13"},
{"key":"MS","value":1,"date":"1/12/13"},
{"key":"RC","value":0,"date":"1/8/13"},
{"key":"RC","value":1,"date":"1/9/13"},
{"key":"RC","value":1,"date":"1/10/13"},
{"key":"RC","value":1,"date":"1/11/13"},
{"key":"RC","value":1,"date":"1/12/13"},
{"key":"CG","value":0,"date":"1/8/13"},
{"key":"CG","value":0,"date":"1/9/13"},
{"key":"CG","value":0,"date":"1/10/13"},
{"key":"CG","value":0,"date":"1/11/13"},
{"key":"CG","value":1,"date":"1/12/13"},
{"key":"RI","value":0,"date":"1/8/13"},
{"key":"RI","value":0,"date":"1/9/13"},
{"key":"RI","value":0,"date":"1/10/13"},
{"key":"RI","value":0,"date":"1/11/13"},
{"key":"RI","value":1,"date":"1/12/13"}
]
chart(data, "pink");
var datearray = [];
var colorrange = [];
function chart(data, color) {
if (color == "blue") {
colorrange = ["#045A8D", "#2B8CBE", "#74A9CF", "#A6BDDB", "#D0D1E6", "#F1EEF6"];
}
else if (color == "pink") {
colorrange = ["#980043", "#DD1C77", "#DF65B0", "#C994C7", "#D4B9DA", "#F1EEF6"];
}
else if (color == "orange") {
colorrange = ["#B30000", "#E34A33", "#FC8D59", "#FDBB84", "#FDD49E", "#FEF0D9"];
}
strokecolor = colorrange[0];
var format = d3.time.format("%m/%d/%y");
var margin = {top: 20, right: 40, bottom: 30, left: 30};
var width = document.body.clientWidth - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
var tooltip = d3.select("body")
.append("div")
.attr("class", "remove")
.style("position", "absolute")
.style("z-index", "20")
.style("visibility", "hidden")
.style("top", "30px")
.style("left", "55px");
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height-10, 0]);
var z = d3.scale.ordinal()
.range(colorrange);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.weeks);
var yAxis = d3.svg.axis()
.scale(y);
var yAxisr = d3.svg.axis()
.scale(y);
var stack = d3.layout.stack()
.offset("silhouette")
.values(function(d) { return d.values; })
.x(function(d) { return d.date; })
.y(function(d) { return d.value; });
var nest = d3.nest()
.key(function(d) { return d.key; });
var area = d3.svg.area()
.interpolate("linear")
.x(function(d) { return x(d.date); })
.y0(function(d) { return y(d.y0); })
.y1(function(d) { return y(d.y0 + d.y); });
var svg = d3.select(".chart").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 + ")");
data.forEach(function(d) {
d.date = format.parse(d.date);
d.value = +d.value;
});
var layers = stack(nest.entries(data));
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function(d) { return area(d.values); })
.style("fill", function(d, i) { return z(i); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + ", 0)")
.call(yAxis.orient("right"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis.orient("left"));
svg.selectAll(".layer")
.attr("opacity", 1)
.on("mouseover", function(d, i) {
svg.selectAll(".layer").transition()
.duration(250)
.attr("opacity", function(d, j) {
return j != i ? 0.6 : 1;
})})
.on("mousemove", function(d, i) {
mousex = d3.mouse(this);
mousex = mousex[0];
var invertedx = x.invert(mousex);
invertedx = invertedx.getMonth() + invertedx.getDate();
var selected = (d.values);
for (var k = 0; k < selected.length; k++) {
datearray[k] = selected[k].date
datearray[k] = datearray[k].getMonth() + datearray[k].getDate();
}
mousedate = datearray.indexOf(invertedx);
pro = d.values[mousedate].value;
d3.select(this)
.classed("hover", true)
.attr("stroke", strokecolor)
.attr("stroke-width", "0.5px"),
tooltip.html( "<p>" + d.key + "<br>" + pro + "</p>" ).style("visibility", "visible");
})
.on("mouseout", function(d, i) {
svg.selectAll(".layer")
.transition()
.duration(250)
.attr("opacity", "1");
d3.select(this)
.classed("hover", false)
.attr("stroke-width", "0px"), tooltip.html( "<p>" + d.key + "<br>" + pro + "</p>" ).style("visibility", "hidden");
})
var vertical = d3.select(".chart")
.append("div")
.attr("class", "remove")
.style("position", "absolute")
.style("z-index", "19")
.style("width", "1px")
.style("height", "380px")
.style("top", "10px")
.style("bottom", "30px")
.style("left", "0px")
.style("background", "#fff");
d3.select(".chart")
.on("mousemove", function(){
mousex = d3.mouse(this);
mousex = mousex[0] + 5;
vertical.style("left", mousex + "px" )})
.on("mouseover", function(){
mousex = d3.mouse(this);
mousex = mousex[0] + 5;
vertical.style("left", mousex + "px")});
}

Related

d3.js - axis arrowhead direction wrong

Arrow head works fine on line draw but direction is wrong on axis!
live_update()
function live_update() {
var data1 = []
var n = 20
for (var i=0;i<=n;i++) {
data1.push({x:i,y:i*i})
}
var data2 = []
for (var i=0;i<=n;i++) {
data2.push({x:i,y:Math.sin(2*Math.PI/n*i)})
}
var toggle = 1
var div = d3.select('body').append('div')
div.append('button')
.text("update")
.on('click',(event,d) => {
console.log('click')
if (toggle == 0) {
update(data1)
toggle = 1
}else{
update(data2)
toggle = 0
}
})
var margin = {top: 10, right: 30, bottom: 30, left: 50},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
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 + ")")
.attr('class','box')
var aw = 6
var ah = 4
svg
.append("defs")
.append("marker")
.attr("id", "arrow")
.attr("refX", aw+1)
.attr("refY", ah/2+1)
.attr("markerWidth", aw+2)
.attr("markerHeight", ah+2)
.attr("orient", "auto")
.append("path")
.attr("d", ['M',1+aw/5,1+ah/2,'L',1,1,1+aw,1+ah/2,1,1+ah,'z'].join(' '));
svg.append('line').attr('class','avline')
var x = d3.scaleLinear().range([0,width]);
var xAxis = d3.axisBottom().scale(x);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("class","myXaxis")
var y = d3.scaleLinear().range([height, 0]);
var yAxis = d3.axisLeft().scale(y);
svg.append("g")
.attr("class","myYaxis")
function update(data) {
var duration = 1000
var xmax = d3.max(data, function(d) { return d.x })
x.domain([d3.min(data,d => d.x), xmax]);
var xaxis = svg.selectAll(".myXaxis").transition()
.duration(duration)
.call(xAxis);
var ymin = d3.min(data,d => d.y)
y.domain([ymin, d3.max(data, function(d) { return d.y }) ]);
var yaxis = svg.selectAll(".myYaxis")
.transition()
.duration(duration)
.call(yAxis);
var frame = d3.select('.box')
frame.selectAll(".lineTest1")
.data([data])
.join("path")
.attr("class","lineTest1")
.transition()
.duration(duration)
.attr("d", d3.line()
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y); }))
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2.5)
.attr("marker-end", "url(#arrow)");
frame.selectAll(".lineTest2")
.data(data)
.join('circle')
.attr('class','lineTest2')
.attr('fill','red')
.transition()
.duration(duration)
.attr('cx',d => x(d.x))
.attr('r',3)
.attr('cy',d => y(d.y))
frame.selectAll(".lineTest3")
.data(data)
.join('path')
.attr('class','lineTest3')
.attr('stroke','gray')
.transition()
.duration(duration)
.attr('d',(d,i) => {
var ax = x(d.x)
var ay = y(d.y)
var bx = ax
var by = y(ymin)
var path = ['M',ax,ay,'L',bx,by]
return path.join(' ')
})
d3.select('.avline')
.attr('stroke','gray')
.transition()
.duration(duration)
.attr('x1',x(0))
.attr('y1',y(0))
.attr('x2',x(xmax))
.attr('y2',y(0))
xaxis.select("path")
.attr("marker-end", "url(#arrow)");
yaxis.select("path")
.attr("marker-end", "url(#arrow)");
}
update(data1)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>

D3.js line chart continuous but want non continuous

Hey guys I created a time series line chart using publicly available stock data.
Where I got to is the following:
Looks like what it is doing is connecting the first datapoint with the last datapoint which is why it is creating a line across the entire chart.
I looked online and read that to create a non continuous line chart I can add
.defined(function(d) { return d; })
I did but it didn't help.
My code:
//Set dimensions and margins for the graph
var margin = {top: 20, right: 20, bottom: 100, left: 70},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//Create 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 + ")");
//Parse date
var parseDate = d3.timeParse("%Y-%m-%d");
//Set the ranges
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
// Define the line
var valueLine = d3.line()
.defined(function(d) { return d; })
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); });
//Import data from api
d3.json("api_all.php", function(error, data) {
if (error) throw error;
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
e.stockName = e.stock_name;
e.stockSymbol = e.stock_symbol;
});
//Create nest variable
var nest = d3.nest()
.key(function(d){ return d.stockSymbol; })
.entries(data);
console.log(nest);
//Scale the range of the data
//x axis scale for entire dataset
x.domain(d3.extent(data, function(d) { return d.date; }));
//y.domain([0, d3.max(data, function(d) { return d.value; })]);
//Add the x axis
var xaxis = svg.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("class", "x axis")
.call(d3.axisBottom(x));
//Add x axis label
svg.append("text")
.attr("transform", "translate(" + (width/2) + "," + (height + margin.top + 10) + ")")
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Date")
.attr("class", "x axis label");
//Create dropdown
var dropDown = d3.select("#dropDown")
dropDown
.append("select")
.selectAll("option")
.data(nest)
.enter()
.append("option")
.attr("value", function(d){ return d.key; })
.text(function(d){ return d.key; })
// Function to create the initial graph
var initialGraph = function(stock){
// Filter the data to include only stock of interest
var selectStock = nest.filter(function(d){
return d.key == stock;
})
console.log(selectStock)
//Unnest selectStock for y axis
var unnested = function(data, children){
var out = [];
data.forEach(function(d, i){
console.log(i, d);
d_keys = Object.keys(d);
console.log(i, d_keys)
values = d[children];
values.forEach(function(v){
d_keys.forEach(function(k){
if (k != children) { v[k] = d[k]}
})
out.push(v);
})
})
return out;
}
var selectStockUnnested = unnested(selectStock, "values");
//Scale y axis
var selectStockGroups = svg.selectAll(".stockGroups")
.data(selectStock, function(d){
return d ? d.key : this.key;
})
.enter()
.append("g")
.attr("class", "stockGroups")
.each(function(d){
y.domain([0, d3.max(selectStockUnnested, function(d) { return d.value; })])
console.log(selectStockUnnested);
});
var initialPath = selectStockGroups.selectAll(".line")
.data(selectStock)
.enter()
.append("path")
initialPath
.attr("d", function(d){ return valueLine(d.values) })
.attr("class", "line")
//Add the y axis
var yaxis = svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y)
.ticks(5)
.tickSizeInner(0)
.tickPadding(6)
.tickSize(0, 0));
//Add y axis label
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - 60)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Price")
.attr("class", "y axis label");
}
// Create initial graph
initialGraph("1301.T")
// Update the data
var updateGraph = function(stock){
// Filter the data to include only stock of interest
var selectStock = nest.filter(function(d){
return d.key == stock;
})
console.log(selectStock);
//Unnest selectStock for y axis
var unnested = function(data, children){
var out = [];
data.forEach(function(d, i){
console.log(i, d);
d_keys = Object.keys(d);
console.log(i, d_keys)
values = d[children];
values.forEach(function(v){
d_keys.forEach(function(k){
if (k != children) { v[k] = d[k]}
})
out.push(v);
})
})
return out;
}
var selectStockUnnested = unnested(selectStock, "values");
// Select all of the grouped elements and update the data
var selectStockGroups = svg.selectAll(".stockGroups")
.data(selectStock)
.each(function(d){
y.domain([0, d3.max(selectStockUnnested, function(d) { return d.value; })])
});
// Select all the lines and transition to new positions
selectStockGroups.selectAll("path.line")
.data(selectStock)
.transition()
.duration(1000)
.attr("d", function(d){
return valueLine(d.values)
})
// Update the Y-axis
d3.select(".y")
.transition()
.duration(1500)
.call(d3.axisLeft(y)
.ticks(5)
.tickSizeInner(0)
.tickPadding(6)
.tickSize(0, 0));
}
// Run update function when dropdown selection changes
dropDown.on('change', function(){
// Find which stock was selected from the dropdown
var selectedStock = d3.select(this)
.select("select")
.property("value")
console.log(selectedStock);
// Run update function with the selected stock
updateGraph(selectedStock)
});
});
</script>
</body>

Trying to transition d3v4 linegraph data from one dataset to a dataset with a different scale

I have a line graph which looks fine. The data set is mostly random data with one big spike. The second dataset is just the log2 of each value. I can transition between the two and it looks great. The y axis is transitioning too. But the scale of the line is not transitioning. Not sure how to get the line to update with the right scale.
$(function() {
var margin = { top: 300, right: 100, bottom: 100, left: 100 },
width = 1400 - margin.right - margin.left,
height = 1080 - margin.top - margin.bottom;
var parseTime = d3.timeParse("%H:%M:%S");
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var log2y = d3.scaleLinear().range([height, 0]);
var valueline = d3.line()
.curve(d3.curveMonotoneX)
.x(function (d) { return x(d.date); })
.y(function (d) { return y(d.value); });
var log2valueline = d3.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return y(Math.log2(d.value)); });
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 + ")");
d3.select("body").attr("align", "center");
d3.csv("data.csv", function (error, data) {
if (error) {
throw error;
}
var log2data = [];
var log10data = [];
data.forEach(function (d, index) {
d.date = parseTime(d.date);
d.value = +d.value;
log2data[index] = { "date": d.date, "value": Math.log2(+d.value) };
log10data[index] = { "date": d.date, "value": Math.log10(+d.value) };
});
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain([0, d3.max(data, function (d) { return d.value; })]);
log2y.domain([0, d3.max(log2data, function (d) { return d.value; }) ]);
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
svg.append("g")
.attr("class", "xaxis")
.attr("class", "chart")
.attr("transform", "translate(0, " + height + ")")
.call(d3.axisBottom(x).ticks(d3.timeMinute.every(10)));
var yaxis = svg.append("g")
.attr("class", "yaxis")
.attr("class", "chart")
.call(d3.axisLeft(y).ticks(5));
yaxis
.transition().duration(500).delay(2000)
.call(d3.axisLeft(log2y).ticks(4));
svg.selectAll("path")
.data([log2data])
.transition().duration(500).delay(2000)
.attr("d", valueline);
});
});
So here is the chart before the transition:
And here is the chart after the transition:
Adding the update from Harpal, I see this:
It's because the new line is still using the old scale (valueline)
Change this:
svg.selectAll("path")
.data([log2data])
.transition().duration(500).delay(2000)
.attr("d", valueline);
to this:
svg.selectAll("path")
.data([log2data])
.transition().duration(500).delay(2000)
.attr("d", log2valueline);
So the solution, as with so many things, is to go for a walk, get something to eat, and return with a fresh look. I rewrote it, based on what I learned thus far and have a working solution:
$(function () {
var margin = { top: 300, right: 100, bottom: 100, left: 100 },
winwidth = $(window).width(),
winheight = $(window).height(),
width = winwidth - margin.right - margin.left,
height = winheight - margin.top - margin.bottom,
x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]);
var parseTime = d3.timeParse("%H:%M:%S");
var valueline = d3.line()
.curve(d3.curveMonotoneX)
.x(function (d) { return x(d.date); })
.y(function (d) { return y(d.value); });
var svg = d3.select('body').append('svg')
.attr("width", winwidth)
.attr("height", winheight)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var path = svg.append("path");
var xaxis = svg.append("g")
.attr("class", "xaxis")
.attr("class", "chart")
.attr("transform", "translate(0, " + height + ")");
var yaxis = svg.append("g")
.attr("class", "yaxis")
.attr("class", "chart");
function step1(sourcedata) {
var data = sourcedata.data;
var y = d3.scaleLinear().range([height, 0]);
y.domain([0, d3.max(data, function (d) { return Math.log2(d.value); })]);
valueline = d3.line()
.curve(d3.curveMonotoneX)
.x(function (d) { return x(d.date); })
.y(function (d) { return y(Math.log2(d.value)); });
path
.data([data])
.transition()
.attr("class", "line")
.attr("d", valueline);
yaxis
.transition()
.call(d3.axisLeft(y).ticks(4));
}
d3.select("body").attr("align", "center");
d3.csv("data.csv", function (error, data) {
if (error) {
throw error;
}
data.forEach(function (d) {
d.date = parseTime(d.date);
d.value = +d.value;
});
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain([0, d3.max(data, function (d) { return d.value; })]);
path
.data([data])
.attr("class", "line")
.attr("d", valueline);
xaxis
.attr("transform", "translate(0, " + height + ")")
.call(d3.axisBottom(x).ticks(d3.timeMinute.every(10)));
yaxis.call(d3.axisLeft(y).ticks(5));
$("#rescale").click(data, step1);
});
});
And the graph works as follows:

Remove old points from line graph in d3

I am trying to create a multi-line graph and updating the data on button click. On each line, I want to highlight the intersection point with a cirlce. Now on button click, I was able to update the line path, but the old highlighted intersection points are not removed from the svgContainer(for eaxmple clicking update2 then update1 do not removes the last set of circles which are not connected to any line).
<input type="button" onclick="update1()" value="Update" />
<input type="button" onclick="update2()" value="UpdateDimension_T" />
<div id="outputViz">
</div>
<script type="text/javascript">
var data = [
[{'index':1,'score':0},{'index':2,'score':5},{'index':3,'score':10},{'index':4,'score':0},{'index':5,'score':6}],
[{'index':1,'score':1},{'index':2,'score':6},{'index':3,'score':11},{'index':4,'score':1},{'index':5,'score':7}],
[{'index':1,'score':2},{'index':2,'score':7},{'index':3,'score':12},{'index':4,'score':2},{'index':5,'score':8}],
[{'index':1,'score':3},{'index':2,'score':8},{'index':3,'score':13},{'index':4,'score':3},{'index':5,'score':9}],
[{'index':1,'score':4},{'index':2,'score':9},{'index':3,'score':14},{'index':4,'score':4},{'index':5,'score':10}]
];
var data_O = [
[{'index':1,'score':1},{'index':2,'score':6},{'index':3,'score':11},{'index':4,'score':1},{'index':5,'score':7},{'index':6,'score':12}],
[{'index':1,'score':2},{'index':2,'score':7},{'index':3,'score':12},{'index':4,'score':2},{'index':5,'score':8},{'index':6,'score':13}],
[{'index':1,'score':3},{'index':2,'score':8},{'index':3,'score':13},{'index':4,'score':3},{'index':5,'score':9},{'index':6,'score':14}],
[{'index':1,'score':4},{'index':2,'score':9},{'index':3,'score':14},{'index':4,'score':4},{'index':5,'score':10},{'index':6,'score':15}],
[{'index':1,'score':5},{'index':2,'score':10},{'index':3,'score':15},{'index':4,'score':5},{'index':5,'score':11},{'index':6,'score':16}]
];
var data_T = [
[{'index':1,'score':5},{'index':2,'score':10},{'index':3,'score':15},{'index':4,'score':5},{'index':5,'score':12},{'index':6,'score':20},{'index':7,'score':15}],
[{'index':1,'score':6},{'index':2,'score':11},{'index':3,'score':16},{'index':4,'score':6},{'index':5,'score':13},{'index':6,'score':21},{'index':7,'score':16}],
[{'index':1,'score':7},{'index':2,'score':12},{'index':3,'score':17},{'index':4,'score':7},{'index':5,'score':14},{'index':6,'score':22},{'index':7,'score':17}],
[{'index':1,'score':8},{'index':2,'score':13},{'index':3,'score':18},{'index':4,'score':8},{'index':5,'score':15},{'index':6,'score':23},{'index':7,'score':18}],
[{'index':1,'score':9},{'index':2,'score':14},{'index':3,'score':19},{'index':4,'score':9},{'index':5,'score':16},{'index':6,'score':24},{'index':7,'score':19}]
];
var colors = [
'steelblue',
'green',
'red',
'purple',
'black'
];
var dataset = ["","Or","Se","Tr","De","Cc"];
var dataset_O = ["","O_1","O_2","O_3","O_4","O_5","O_6"];
var dataset_T = ["","T_1","T_2","T_3","T_4","T_5","T_6","T_7"];
var margin = {top: 20, right: 30, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
padding = 30;
var x = d3.scale.linear()
.domain([0, dataset.length])
.range([0, width]);
var y = d3.scale.linear()
.domain([-1, 16])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickFormat(function(d) { return dataset[d]; })
.tickSize(-height)
.tickPadding(10)
.tickSubdivide(false)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.tickPadding(10)
.tickSize(-width)
.tickSubdivide(false)
.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.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.append("text")
.attr("class", "axis-label")
.attr("transform", "rotate(-90)")
.attr("y", (-margin.left) + 10)
.attr("x", -height/2)
.text('Axis Label');
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { return x(d.index); })
.y(function(d) { return y(d.score); });
svg.selectAll('.line')
.data(data)
.enter()
.append("path")
.attr("class", "line")
.attr('stroke', function(d,i){
return colors[i%colors.length];
})
.attr("d", line);
var points = svg.selectAll('.dots')
.data(data)
.enter()
.append("g")
.attr("class", "dots")
points.selectAll('.dot')
.data(function(d, index){
var a = [];
d.forEach(function(point,i){
a.push({'index': index, 'point': point});
});
return a;
})
.enter()
.append('circle')
.attr('class','dot')
.attr("r", 2.5)
.attr('fill', function(d,i){
return colors[d.index%colors.length];
})
.attr("transform", function(d) {
return "translate(" + x(d.point.index) + "," + y(d.point.score) + ")"; }
);
function update1(){
var x = d3.scale.linear()
.domain([0, dataset_O.length])
.range([0, width]);
var y = d3.scale.linear()
.domain([-1, 16])
.range([height, 0]).nice();
xAxis = d3.svg.axis()
.scale(x)
.tickFormat(function(d) { return dataset_O[d]; })
.tickSize(-height)
.tickPadding(10)
.tickSubdivide(false)
.orient("bottom");
yAxis = d3.svg.axis()
.scale(y)
.tickPadding(10)
.tickSize(-width)
.tickSubdivide(false)
.orient("left");
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { return x(d.index); })
.y(function(d) { return y(d.score); });
svg.selectAll('.line')
.data(data_O)
.transition(750)
.attr("d", line)
.attr("class", "line");
// change the x axis
svg.select(".x.axis").call(xAxis);
// change the y axis
svg.select(".y.axis").call(yAxis);
var points = svg.selectAll('.dots').data(data_O);
//UPDATE - HANDLE the current count
points.selectAll('.dot')
.data(function(d, index){
var a = [];
d.forEach(function(point,i){
a.push({'index': index, 'point': point});
});
return a;
})
.attr("transform", function(d) {
return "translate(" + x(d.point.index) + "," + y(d.point.score) + ")";
});
//ENTER - add the newly added count
points.selectAll('.dot')
.data(function(d, index){
var a = [];
d.forEach(function(point,i){
a.push({'index': index, 'point': point});
});
return a;
})
.enter()
.append('circle')
.attr('class','dot')
.attr("r", 2.5)
.attr('fill', function(d,i){
return colors[d.index%colors.length];
})
.attr("transform", function(d) {
return "translate(" + x(d.point.index) + "," + y(d.point.score) + ")";
});
d3.selectAll('g.dots').data(data_O).exit().remove();
}
function update2(){
var x = d3.scale.linear()
.domain([0, dataset_T.length])
.range([0, width]);
//var yExtents = d3.extent(d3.merge(data_T), function (d) { return d.score; });
var y = d3.scale.linear()
.domain([-1, 29])
.range([height, 0]).nice();
xAxis = d3.svg.axis()
.scale(x)
.tickFormat(function(d) { return dataset_T[d]; });
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { return x(d.index); })
.y(function(d) { return y(d.score); });
svg.selectAll('.line')
.data(data_T)
.transition(750)
.attr("d", line)
.attr("class", "line");
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
var points = svg.selectAll('.dots').data(data_T);
//ENTER - add the newly added count
points.selectAll('.dot')
.data(function(d, index){
var a = [];
d.forEach(function(point,i){
a.push({'index': index, 'point': point});
});
return a;
})
.enter()
.append('circle')
.attr('class','dot')
.attr("r", 2.5)
.attr('fill', function(d,i){
return colors[d.index%colors.length];
})
.attr("transform", function(d) {
return "translate(" + x(d.point.index) + "," + y(d.point.score) + ")";
});
//UPDATE - HANDLE the current count
points.selectAll('.dot')
.data(function(d, index){
var a = [];
d.forEach(function(point,i){
a.push({'index': index, 'point': point});
});
return a;
})
.attr("transform", function(d) {
return "translate(" + x(d.point.index) + "," + y(d.point.score) + ")";
});
}
</script>
Here is the link to fiddle:
https://jsfiddle.net/aakashjain/1dc57aL7/1/
You'll need an "exit" selection:
points.selectAll('.dot')
.data(function(d, index){
var a = [];
d.forEach(function(point,i){
a.push({'index': index, 'point': point});
});
return a;
})
.exit()
.remove();
Here is the update fiddle:. https://jsfiddle.net/1dc57aL7/2/
(Just a tip: you have a lot of duplicated code here. Your "update1" and "update2" functions could be way smaller)

add scrollbar and navigator, range selector functionality in d3.js

i want to include scrollbar and navigator, range selector functionality in my line graph . something similar to http://www.highcharts.com/products/highstock Can this be done on d3 if so give me a link on a tutorial or code.
the code is
var margin = {top: 10, right: 10, bottom: 100, left: 40},
margin2 = {top: 430, right: 10, bottom: 30, left: 40},
width = 2000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%Y-%m-%d").parse;
var formatTime = d3.time.format("%e %B");
var x = d3.time.scale().range([0, width]);
var x2= d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var y2 = d3.scale.linear().range([height2, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(25);
var xAxis2 = d3.svg.axis().scale(x2).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
var brush = d3.svg.brush()
.x(x2)
.on("brush", brushed);
var valueline = d3.svg.line().defined(function(d) { return d.close != 0; }).x(function(d) { return x(d.date); }).y(function(d) { return y(d.close); });
var valueline2 = d3.svg.line().defined(function(d) { return d.close != 0; }).x(function(d) { return x2(d.date); }).y(function(d) { return y2(d.close); });
var brush = d3.svg.brush()
.x(x2)
.on("brush", brushed);
var svg = d3.select("body").append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
d3.json("data/data2.php", function(error, data) { data.forEach(function(d,i) {
//document.write(d.date);
d.date = parseDate(d.date);
//document.write(d.date);
d.close = +d.close;
//document.write(d.close);
arr[i]=d.close;
len=i+1;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([min-10, max+10]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path") // Add the valueline path.
.datum(data)
.attr("class","valueline")
.attr("d", valueline);
focus.selectAll("dot")
.data(data)
.enter().append("a")
.attr("xlink:href",function(d,i){if(d.close>=usl||d.close<=lsl||signal8[i]==8||signal8dw[i]==8||signal6up[i]==6||signal6dw[i]==6)return "http://en.wikipedia.org";})
.append("circle")
.attr("r", 2)
.style("fill", function(d,i) { // <== Add these
if(d.close==0) {return "none"}
if((ul[i]==9999)||(dl[i]==9999)) {return "red"}
else if(signal8[i]==8 ){ return "orange" }
else if(signal8dw[i]==8 ){return "gold"}
else if(signal6up[i]==6 ){return "indianred"}
else if(signal6dw[i]==6 ){return "#FF5C33"}
else { return "steelblue" } // <== Add these
;})
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.close); })
.on("mouseover", function(d,i) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(function(){
if(d.close==0)
{return}
if(ul[i]==9999)
{return formatTime(d.date) + "<br/><b>" + d.close+ "</b><br/>" + " UPPER "}
else if(dl[i]==9999)
{return formatTime(d.date) + "<br/><b>" + d.close+ "</b><br/>" + " LOWER "}
else if(signal8[i]==8)
{return formatTime(d.date) + "<br/><b>" + d.close+ "</b><br/>" + " ( hello1 )"}
else if(signal8dw[i]==8)
{return formatTime(d.date) + "<br/><b>" + d.close+ "</b><br/>" + " ( hello1 )"}
else if(signal6up[i]==6)
{return formatTime(d.date) + "<br/><b>" + d.close+ "</b><br/>" + " ( hello1 )"}
else if(signal6dw[i]==6)
{return formatTime(d.date) + "<br/><b>" + d.close+ "</b><br/>" + " ( hello1 )"}
else {return formatTime(d.date) + "<br/><b>" + d.close+ "</b>"}
;})
context.append("path") // Add the valueline path.
.attr("d", valueline2(data));
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
});
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focus.select(".valueline").attr("d",valueline);
focus.select(".x.axis").call(xAxis);
}
function type(d) {
d.date = parseDate(d.date);
d.close = +d.close;
return d;
}
the above code is a snippet of the overall code. here when im executing im getting a parse error d="". where am i wrong. and i have drawn a few limit lines in the graph which i have not included in the above code. the bottom brush is working but the main graph is not getting updated as per the brush.
i want to update the dots too. what should i include the brushed function.
For the point marks, you again need a selectAll, and then reset the cx property to match the modified x scale:
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
//change the x-scale on the focus graph to match the brush extent
//(or reset it to the full domain if the brush is empty)
focus.selectAll(".valueline").attr("d",valueline);
// redraw the lines (using the updated scale)
focus.selectAll("dot").select("circle")
.attr("cx", function(d) { return x(d.date); });
//update the x position of the dots based on the updated x-scale
focus.select(".x.axis").call(xAxis);
//redraw the x-axis based on the updated x-scale
}
var margin = {top: 10, right: 10, bottom: 100, left: 40},
margin2 = {top: 430, right: 10, bottom: 30, left: 40},
width = 2000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%Y-%m-%d").parse;
var formatTime = d3.time.format("%e %B");
var x = d3.time.scale().range([0, width]);
var x2= d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var y2 = d3.scale.linear().range([height2, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(25);
var xAxis2 = d3.svg.axis().scale(x2).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
var brush = d3.svg.brush()
.x(x2)
.on("brush", brushed);
var valueline = d3.svg.line().defined(function(d) { return d.close != 0; }).x(function(d) { return x(d.date); }).y(function(d) { return y(d.close); });
var valueline2 = d3.svg.line().defined(function(d) { return d.close != 0; }).x(function(d) { return x2(d.date); }).y(function(d) { return y2(d.close); });
var brush = d3.svg.brush()
.x(x2)
.on("brush", brushed);
var svg = d3.select("body").append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
d3.json("data/data2.php", function(error, data) { data.forEach(function(d,i) {
//document.write(d.date);
d.date = parseDate(d.date);
//document.write(d.date);
d.close = +d.close;
//document.write(d.close);
arr[i]=d.close;
len=i+1;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([min-10, max+10]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path") // Add the valueline path.
.datum(data)
.attr("class","valueline")
.attr("d", valueline);
context.append("path") // Add the valueline path.
.attr("d", valueline2(data));
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
});
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focus.selectAll(".valueline").attr("d",valueline); // selectAll is the answer
focus.select(".x.axis").call(xAxis);
}
function type(d) {
d.date = parseDate(d.date);
d.close = +d.close;
return d;
}

Resources