D3 Animate One Path Series At a Time - javascript

Learning Javascript and D3.
Trying to create a graph where it draws each line in a series, one at a time or on a delayed interval.
What I've got so far (relevant code at the end of this post)
https://jsfiddle.net/jimdholland/5n6xrLk0/180/
My data is structures with each series as one row, with 12 columns. When you read it in, the object approximately looks like
mydata = [
{NumDays01: "0", NumDays02: "0", NumDays03: "0", NumDays04: "0", NumDays05: "0",Numdays06: 30}
1: {NumDays01: "0", NumDays02: "0", NumDays03: "0", NumDays04: "0",...
]
I can get it to create line, but it draws all the paths at once. Likewise, I've tried a loop, but that still draws them all at once. Also tried d3.timer and similar results. But I'm having a hard time understanding the timer callback stuff and creating those functions correctly.
I haven't found another example to really study other than a chart from FlowingData which has a lot of bells and whistles out of my league.
https://flowingdata.com/2019/02/01/how-many-kids-we-have-and-when-we-have-them/
Any help or tips would be appreciated.
var svg = d3.select("#chart").select("svg"),
margin = { top: 30, right: 20, bottom: 10, left: 40 },
width = parseInt(d3.select("#chart").style('width'), 10) - margin.left - margin.right;
d3.tsv("https://s3.us-east-2.amazonaws.com/jamesdhollandwebfiles/data/improvementTest.tsv", function(error, data) {
if (error) throw error;
console.log(data);
myData = data;
var t = d3.timer(pathMaker);
}); // #end d3.tsv()
function pathMaker() {
var peeps = myData[rowToRun];
var coords = [];
var lineToRemove = [];
for (var nameIter in dayList) {
coords.push({ x: x(nameIter), y: y(peeps[dayList[nameIter]])})
}
var improvementPath = g.append("path")
.datum(coords)
.attr("id", "path" + peeps.caseid)
.attr("d", lineMaker)
.attr("stroke", "#90c6e4")
.attr("fill", "none")
.attr("stroke-width", 2);
var total_length = improvementPath.node().getTotalLength();
var startPoint = pathStartPoint(improvementPath);
improvementPath = improvementPath
.attr("stroke-dasharray", total_length + " " + total_length)
.attr("stroke-dashoffset", total_length)
.transition() // Call Transition Method
.duration(4000) // Set Duration timing (ms)
.ease(d3.easeLinear) // Set Easing option
.attr("stroke-dashoffset", 0); // Set final value of dash-offset for transition
rowToRun += 1;
if (rowToRun == 5) {rowToRun = 0;}
}
//Get path start point for placing marker
function pathStartPoint(Mypath) {
var d = Mypath.attr("d"),
dsplitted = d.split(/M|L/)[1];
return dsplitted;
}

there are a few problems. I tried to configure your code as little as I could to make it work. If you need further explanations, please let me know
d3.tsv("https://s3.us-east-2.amazonaws.com/jamesdhollandwebfiles/data/improvementTest.tsv", function(error, data) {
if (error) throw error;
console.log(data);
myData = data;
pathMaker()
}); // #end d3.tsv()
function pathMaker() {
var peeps = myData[rowToRun];
var coords_data = [];
var lineToRemove = [];
for (let i = 0; i < myData.length; i++) {
var coords = [];
for (var nameIter in dayList) {
coords.push({ x: x(nameIter), y: y(myData[i][dayList[nameIter]])})
}
coords_data.push(coords)
}
console.log(coords_data)
var improvementPath = g.selectAll("path")
.data(coords_data)
.enter()
.append("path")
.attr("d", lineMaker)
.attr("stroke", "#90c6e4")
.attr("fill", "none")
.attr("stroke-width", 2);
improvementPath = improvementPath.each(function (d,i) {
var total_length = this.getTotalLength();
var startPoint = pathStartPoint(improvementPath);
const path = d3.select(this)
.attr("stroke-dasharray", total_length + " " + total_length)
.attr("stroke-dashoffset", total_length)
.transition() // Call Transition Method
.duration(4000) // Set Duration timing (ms)
.delay(i*4000)
.ease(d3.easeLinear) // Set Easing option
.attr("stroke-dashoffset", 0); // Set final value of dash-offset for transition
})
rowToRun += 1;
if (rowToRun == 5) {rowToRun = 0;}
}

Related

D3: Combine two colour scales into one

I currently have two variables that I can map to two different colours on two colours scales s1 and s2. s1 gives me the shade of red corresponding to my a value of my variable X (4 different possible colours). s1 gives me the shade of blue corresponding to my a value of my variable Y (4 different possible colours too).
Now what I would like to get is something that allows me to combine these two to get a unique colour for a combination of the variables. So for a pair (X,Y) I get a colour on the scale. So I get a scale of 16 possible colours.
Here is a legend that illustrate the kind of thing I am looking for:
I have been looking at online examples but cannot figure out how to achieve this.
You could combine two threshold scales fairly easily into a new scale function. The core of the function could look like:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(value[0]);
var b = blues(value[1]);
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
return scaleBivariate;
}
This sets the red and blue channels with the help of two d3 threshold scales. The green is simply set as the average between the two, though you could set that to whatever is desirable, say 0 or the minimum of the two other channels. My red/blue ranges are arbitrary and easily changed as well.
The above could be used as:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(value[0]);
var b = blues(value[1]);
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
return scaleBivariate;
}
// Dummy data:
var data = d3.range(16).map(function(d) {
return {x: d%4, y: Math.floor(d/4) }
})
var svg = d3.select("svg");
var size = 30;
var color = d3.scaleBivariate();
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return d.x * size })
.attr("y", function(d) { return d.y * size })
.attr("width",size)
.attr("height",size)
.attr("fill",function(d) {
return color([d.x,d.y]);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
<svg></svg>
Of course you might want to add some flexibility by adding methods to modify what datum property sets what colors are associated with what properties, what the thresholds should be, etc. To provide a basic example, the example below has added accessors for setting what property should be mapped to blue and red channels:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(red(value));
var b = blues(blue(value));
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var red = function(d) { return d[0]; }
var blue = function(d) { return d[1];}
// Accessors:
scaleBivariate.red = function(_) {
return arguments.length ? (red = _, scaleBivariate): red;
}
scaleBivariate.blue = function(_) {
return arguments.length ? (blue = _, scaleBivariate): blue;
}
return scaleBivariate;
}
var data = d3.range(16).map(function(d) {
return {x: d%4, y: Math.floor(d/4) }
})
var svg = d3.select("svg");
var size = 30;
// set up the color scale:
var color = d3.scaleBivariate()
.red(function(d) { return d.x; })
.blue(function(d) { return d.y; });
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return d.x * size })
.attr("y", function(d) { return d.y * size })
.attr("width",size)
.attr("height",size)
.attr("fill",color);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
<svg></svg>

D3.js Problem rendering barchart with object data

I have the following script for rendering a simple barchart in D3.js. I have been able to render charts ok up until a point.
I have this data below which I am struggling to insert into my chart, there is no specific key I can call upon and I'm really confused how I would insert all of these into my chart.
Object { "food-environmental-science": 0, "art-media-research": 0, .....}
I have a seperate file for the HTML (only a snippet):
var barchart1 = barchart("#otherchart");
function clickScatter(d){
var unitOfAssessment = d.UoAString;
click = d.environment.topicWeights
renderTopicWeights(click)
}
function renderTopicWeights(clickedPoint){
barchart1.loadAndRenderDataset(clickedPoint)
}
When I call upon the loadAndRenderDataset function, console gives me a data.map is not a function error.
function barchart(targetDOMelement) {
//=================== PUBLIC FUNCTIONS =========================
//
barchartObject.appendedMouseOverFunction = function (callbackFunction) {
console.log("appendedMouseOverFunction called", callbackFunction)
appendedMouseOverFunction = callbackFunction;
render();
return barchartObject;
}
barchartObject.appendedMouseOutFunction = function (callbackFunction) {
appendedMouseOutFunction = callbackFunction;
render();
return barchartObject;
}
barchartObject.loadAndRenderDataset = function (data) {
dataset=data.map(d=>d); //create local copy of references so that we can sort etc.
render();
return barchartObject;
}
barchartObject.overrideDataFieldFunction = function (dataFieldFunction) {
dataField = dataFieldFunction;
return barchartObject;
}
barchartObject.overrideKeyFunction = function (keyFunction) {
//The key function is used to obtain keys for GUP rendering and
//to provide the categories for the y-axis
//These valuse should be unique
GUPkeyField = yAxisCategoryFunction = keyFunction;
return barchartObject;
}
barchartObject.overrideMouseOverFunction = function (callbackFunction) {
mouseOverFunction = callbackFunction;
render();
return barchartObject;
}
barchartObject.overrideMouseOutFunction = function (callbackFunction) {
mouseOutFunction = callbackFunction;
render(); //Needed to update DOM
return barchartObject;
}
barchartObject.overrideTooltipFunction = function (toolTipFunction) {
tooltip = toolTipFunction;
return barchartObject;
}
barchartObject.overrideMouseClickFunction = function (fn) {
mouseClick2Function = fn;
render(); //Needed to update DOM if they exist
return barchartObject;
}
barchartObject.render = function (callbackFunction) {
render(); //Needed to update DOM
return barchartObject;
}
barchartObject.setTransform = function (t) {
//Set the transform on the svg
svg.attr("transform", t)
return barchartObject;
}
barchartObject.yAxisIndent = function (indent) {
yAxisIndent=indent;
return barchartObject;
}
//=================== PRIVATE VARIABLES ====================================
//Width and height of svg canvas
var svgWidth = 900;
var svgHeight = 450;
var dataset = [];
var xScale = d3.scaleLinear();
var yScale = d3.scaleBand(); //This is an ordinal (categorical) scale
var yAxisIndent = 400; //Space for labels
var maxValueOfDataset; //For manual setting of bar length scaling (only used if .maxValueOfDataset() public method called)
//=================== INITIALISATION CODE ====================================
//Declare and append SVG element
var svg = d3
.select(targetDOMelement)
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
.classed("barchart",true);
//Declare and add group for y axis
var yAxis = svg
.append("g")
.classed("yAxis", true);
//Declare and add group for x axis
var xAxis = svg
.append("g")
.classed("xAxis", true);
//===================== ACCESSOR FUNCTIONS =========================================
var dataField = function(d){return d.datafield} //The length of the bars
var tooltip = function(d){return d.key + ": "+ d.datafield} //tooltip text for bars
var yAxisCategoryFunction = function(d){return d.key} //Categories for y-axis
var GUPkeyField = yAxisCategoryFunction; //For 'keyed' GUP rendering (set to y-axis category)
//=================== OTHER PRIVATE FUNCTIONS ====================================
var maxValueOfDataField = function(){
//Find the maximum value of the data field for the x scaling function using a handy d3 max() method
//This will be used to set (normally used )
return d3.max(dataset, dataField)
};
var appendedMouseOutFunction = function(){};
var appendedMouseOverFunction = function(){};
var mouseOverFunction = function (d,i){
d3.select(this).classed("highlight", true).classed("noHighlight", false);
appendedMouseOverFunction(d,i);
}
var mouseOutFunction = function (d,i){
d3.select(this).classed("highlight", false).classed("noHighlight", true);
appendedMouseOutFunction(d,i);
}
var mouseClick2Function = function (d,i){
console.log("barchart click function = nothing at the moment, d=",d)
};
function render () {
updateScalesAndRenderAxes();
GUP_bars();
}
function updateScalesAndRenderAxes(){
//Set scales to reflect any change in svgWidth, svgHeight or the dataset size or max value
xScale
.domain([0, maxValueOfDataField()])
.range([0, svgWidth-(yAxisIndent+10)]);
yScale
.domain(dataset.map(yAxisCategoryFunction)) //Load y-axis categories into yScale
.rangeRound([25, svgHeight-40])
.padding([.1]);
//Now render the y-axis using the new yScale
var yAxisGenerator = d3.axisLeft(yScale);
svg.select(".yAxis")
.transition().duration(1000).delay(1000)
.attr("transform", "translate(" + yAxisIndent + ",0)")
.call(yAxisGenerator);
//Now render the x-axis using the new xScale
var xAxisGenerator = d3.axisTop(xScale);
svg.select(".xAxis")
.transition().duration(1000).delay(1000)
.attr("transform", "translate(" + yAxisIndent + ",20)")
.call(xAxisGenerator);
};
function GUP_bars(){
//GUP = General Update Pattern to render bars
//GUP: BIND DATA to DOM placeholders
var selection = svg
.selectAll(".bars")
.data(dataset, GUPkeyField);
//GUP: ENTER SELECTION
var enterSel = selection //Create DOM rectangles, positioned # x=yAxisIndent
.enter()
.append("rect")
.attr("x", yAxisIndent)
enterSel //Add CSS classes
.attr("class", d=>("key--"+GUPkeyField(d)))
.classed("bars enterSelection", true)
.classed("highlight", d=>d.highlight)
enterSel //Size the bars
.transition()
.duration(1000)
.delay(2000)
.attr("width", function(d) {return xScale(dataField(d));})
.attr("y", function(d, i) {return yScale(yAxisCategoryFunction(d));})
.attr("height", function(){return yScale.bandwidth()});
enterSel //Add tooltip
.append("title")
.text(tooltip)
//GUP UPDATE (anything that is already on the page)
var updateSel = selection //update CSS classes
.classed("noHighlight updateSelection", true)
.classed("highlight enterSelection exitSelection", false)
.classed("highlight", d=>d.highlight)
updateSel //update bars
.transition()
.duration(1000)
.delay(1000)
.attr("width", function(d) {return xScale(dataField(d));})
.attr("y", function(d, i) {return yScale(yAxisCategoryFunction(d));})
.attr("height", function(){return yScale.bandwidth()});
updateSel //update tool tip
.select("title") //Note that we already created a <title></title> in the Enter selection
.text(tooltip)
//GUP: Merged Enter & Update selections (so we don't write these twice)
var mergedSel = enterSel.merge(selection)
.on("mouseover", mouseOverFunction)
.on("mouseout", mouseOutFunction)
.on("click", mouseClick2Function)
//GUP EXIT selection
var exitSel = selection.exit()
.classed("highlight updateSelection enterSelection", false)
.classed("exitSelection", true)
.transition()
.duration(1000)
.attr("width",0)
.remove()
};
return barchartObject;'
}
Any help would be much appreciated, and I appolguise if what I'm asking is not clear. Thanks
format your input data into object like {key:"",value:""} and pass this into d3 so that they can understand and render chart.
var input = {'a':1,"b":2};
function parseData(input){
return Object.keys(input).reduce(function(output, key){
output.push({"key":key,"value":input[key]});
return output;
},[])
}
console.log(parseData(input));
// [{"key":"a","value":1},{"key":"b","value":2}]
jsFiddle demo - https://jsfiddle.net/1kdsoyg2/1/

D3.js adding/removing data pie chart slices

I have an input control that should on the key up event on the control either add or subtract a slice from a pie chart depending on if you hit the left or right arrow keys. It doesn't really work as expected because it seems that the new pie slice is sometimes being taken out of existing pie slices and the entire chart is not being recalculated. I'm not sure if I'm messing up the enter or the exit selection. Help is appreciated, TIA.
fiddle http://jsfiddle.net/jaqj3usb/
pertinent code
function removeSlices(n){
var i = -1;
while(++i < n){
data.pop();
}
paths.data(pie(data)).exit().remove();
paths.data(pie(data)).attr('d',arc).attr('transform','translate(100,200)')
}
function changeSlices(){
console.log('woot');
var shouldAddSlice = d3.event.target.valueAsNumber > data.length,
numDifference = Math.abs(data.length - d3.event.target.valueAsNumber);
if(shouldAddSlice){
addSlices(numDifference);
}
else{
removeSlices(numDifference);
}
}
sliceSlider.on('keyup',changeSlices);
I am also new to D3js, trying to learn. I used jQuery for handling keyup events. I did some changes, so left arrow adds and right arrow subtracts slices.
passed jquery as in argument to self executing function
(function(d3, $) {
})(window.d3, $);
In addSlices() changed
paths
.data(pie(data))
.enter()
.append('path')
.attr('d',arc)
.attr('transform','translate(100,200)')
.attr('fill',function(d,i){
return colors[i]
});
to
paths.data(pie([])).exit().remove();
paths = svg.selectAll('path').append('g')
.data(pie(data))
.enter()
.append('path')
.attr('d',arc)
.attr('transform','translate(100,200)')
.attr('fill',function(d,i){
return colors[i];
});
above code deletes and redraws the pie.
full code:
(function(d3,$){
var svg = d3.select('body').append('svg').attr({
height: 500,
width: 500,
id: 'mySvg'
});
var data = [10,20,30];
var sliceSlider = d3.select('body').append('input').attr({
type: 'range',
max: 10,
min: 1,
value : data.length
});
var colors=['orange','blue','steelblue','green','red','yellow','purple','pink','indigo'];
var pie = d3.layout.pie();
pie.value(function(d){ return d});
var arc = d3.svg.arc().innerRadius(10).outerRadius(100);
var paths = svg.selectAll('path').append('g')
.data(pie(data))
.enter()
.append('path')
.attr('d',arc)
.attr('transform','translate(100,200)')
.attr('fill',function(d,i){
return colors[i]
});
function addSlices(n){
var i = -1;
function randomBetween(min,max){
return Math.floor(Math.random()*(max-min+1)+min);
}
while(++i <n){
data.push(randomBetween(10,50));
}
paths.data(pie([])).exit().remove();
paths = svg.selectAll('path').append('g')
.data(pie(data))
.enter()
.append('path')
.attr('d',arc)
.attr('transform','translate(100,200)')
.attr('fill',function(d,i){
return colors[i];
});
}
function removeSlices(n){
var i = -1;
while(++i < n){
data.pop();
}
paths.data(pie(data)).exit().remove();
paths.data(pie(data)).attr('d',arc).attr('transform','translate(100,200)');
}
function changeSlices(){
console.log('woot');
var max=$("input[type='range']").attr("max")-data.length;
var shouldAddSlice = max > data.length;
if(shouldAddSlice){
addSlices(1);
}
}
$(document).keyup(function(e) {
switch(e.which) {
case 37: // left
changeSlices();
break;
case 39: // right
removeSlices(1);
break;
default: return; // exit this handler for other keys
}
e.preventDefault(); // prevent the default action (scroll / move caret)
});
})(window.d3,$);
example : http://jsfiddle.net/jaqj3usb/2/
Here is first variant (I think this is correct), base on this please add comments because you are not computing correctly from the start svg's added to the page.
You can remove individual pie slice like this but this will is not update correct your plate:
svg.selectAll('path')[0][pos].remove();
where pos 0 < data.length (in your original code not correct)
Adding in this way you are overlapping unmoved slices:
paths
.data(pie(data))
.enter()
.append('path')
.attr('d',arc)
.attr('transform','translate(100,200)')
.attr('fill',function(d,i){
return colors[i]
});
Anyhow:
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Eat my pie</title>
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.js"></script>
<script type='text/javascript'>
window.onload=function(){
/**
* Created by cecild on 5/21/2015.
* Updated by SilentTremor 5/25/2015.
*/
(function(d3){
svg = d3.select('body').append('svg').attr({
height: 800,
width: 900,
id: 'mySvg'
});
var data = [10];
var sliceSlider = d3.select('body').append('input').attr({
type: 'range',
max: 10,
min: 1,
value : data.length
});
var colors=['orange','blue','steelblue','green','red','yellow','purple','pink','indigo'];
var pie = d3.layout.pie();
pie.value(function(d){ return d});
var arc = d3.svg.arc().innerRadius(10).outerRadius(100);
var paths = svg.selectAll('path').append('g')
.data(pie(data))
.enter()
.append('path')
.attr('d',arc)
.attr('transform','translate(100,200)')
.attr('fill',function(d,i){
return colors[i]
});
function addSlices(n){
var i = -1;
function randomBetween(min,max){
return Math.floor(Math.random()*(max-min+1)+min);
}
while(++i <n){
data.push(randomBetween(10,50));
}
svg.selectAll("path").remove();
paths = svg.selectAll('path').append('g')
.data(pie(data))
.enter()
.append('path')
.attr('d',arc)
.attr('transform','translate(100,200)')
.attr('fill',function(d,i){
return colors[i]
});
}
function removeSlices(n){
var i = -1;
while(++i < n){
data.pop();
}
svg.selectAll("path").remove()
paths = svg.selectAll('path').append('g')
.data(pie(data))
.enter()
.append('path')
.attr('d',arc)
.attr('transform','translate(100,200)')
.attr('fill',function(d,i){
return colors[i]
});
}
function changeSlices(){
var shouldAddSlice = d3.event.target.valueAsNumber > data.length,
numDifference = Math.abs(data.length - d3.event.target.valueAsNumber);
if(shouldAddSlice){
addSlices(numDifference);
}
else{
removeSlices(numDifference);
}
}
sliceSlider.on('keyup',changeSlices);
})(window.d3);
}
</script>
</head>
<body>
</body>
</html>
In the same way that you use this code:
svg.selectAll('path').append('g')
.data(pie(data))
.enter()
.append('path')
You can use:
svg.selectAll('path').append('g')
.data(pie(data))
.exit()
.remove()
This should remove pie graph elements which no longer have data associated with them.

D3 Candlestick chart date on xaxis

I am trying to create candlestick chart using d3.js, it works great but I cannot solve a problem. I am sure it is covered somewhere but I cannot find it.
I have candlestick data, here is one record:
{
"Date":"02/01/2010",
"Time":1265004000,
"YDay":31,
"Open":1.571500,
"High":1.582100,
"Low":1.558700,
"Close":1.580500,
"SMA":1.5663,
"EMA":1.56743786563595
},
As you can see, the real xaxis is the time value which is epoch time (# of seconds since 1/1/70). I need it working with those seconds to properly format the chart, but I want the x axis labeled with "Date" value instead of the seconds. With this code the plot is great but the x axis is just a long set of numbers (the # of seconds overwriting each other).
How can I get it to do the calculations in seconds, but display the dates on the x axis.
Here is a self contained html file with embedded data. Just cut and paste into index.html on your disk and double click.
<html>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var xs;
var ys;
var base_width = 960;
var base_height = 600;
var x_min;
var x_max;
var y_min;
var y_max;
var barWidth;
var candleWidth;
var xAxis;
var yAxis;
var margin = {top: 20, right: 30, bottom: 30, left: 40};
function getYLow (d)
{
return ys (d.Low);
}
function getYHigh (d)
{
return ys (d.High);
}
function getYOC (d)
{
var yOpen = ys (d.Open);
var yClose = ys (d.Close);
if (yOpen < yClose)
{
return yOpen;
}
return yClose;
}
function getYHeight (d)
{
var yHeight = ys (d.Open) - ys (d.Close);
if (yHeight < 0)
{
yHeight *= -1;
}
return yHeight;
}
function getX (d)
{
// offset by half the width of my candlestick rectangle
// but centered on the actual time
var x = xs (d.Time) - (candleWidth / 2.0);
return x;
}
function getXActual (d)
{
var x = xs (d.Time);
return x;
}
function getColor (d)
{
// I want the candlestick to be green if it closed higher
// then the open otherwise it should be red
if (d.Close > d.Open)
return "green";
return "red";
}
function load ()
{
// load the data and create the chart
width = base_width - margin.left - margin.right,
height = base_height - margin.top - margin.bottom;
ys = d3.scale.linear ().range ([height, 0]);
xs = d3.scale.linear ().range ([0, width]);
var svg = d3.select ("body").append ("svg")
.attr ("width", width + margin.left + margin.right)
.attr ("height", height + margin.top + margin.bottom)
.style ("background-color", "black")
.append ("g")
.attr ("transform", "translate(" + margin.left + "," + margin.top + ")");
xAxis = d3.svg.axis()
.scale(xs)
.orient("bottom");
yAxis = d3.svg.axis()
.scale(ys)
.orient("left");
y_min = d3.min (data, function (d) { return d.Low; });
y_max = d3.max (data, function (d) { return d.High; });
x_min = data [0].Time;
x_max = data [data.length - 1].Time;
// offset min and max by 1 day to give some margin so the candlestick
// rectangle stays on the chart and does not cross the axis
x_min = +x_min - 86400;
x_max = +x_max + 86400;
ys.domain ([y_min, y_max]);
xs.domain ([x_min, x_max]);
// this is the actual width of 1 day (1 bar)
barWidth = xs (+x_min + 86400) - xs (+x_min);
// the candle should be 75% the width of the day
candleWidth = barWidth * 0.75;
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.attr("stroke", "white")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("stroke", "white")
.call(yAxis);
svg.selectAll (".candle_bar")
.data (data)
.enter ().append ("rect")
.attr ("class", "candle_bar")
.attr ("x", getX)
.attr ("y", getYOC)
.attr ("height", getYHeight)
.attr ("width", candleWidth)
.attr ("fill", getColor);
svg.selectAll ("candle_hl")
.data (data)
.enter ().append ("svg:line")
.attr ("x1", getXActual)
.attr ("y1", getYLow)
.attr ("x2", getXActual)
.attr ("y2", getYHigh)
.style ("stroke", getColor);
// for plotting an sma and ema line
var line_sma = d3.svg.line ().x (function (d, i) { return xs (d.YDay); })
.y (function (d, i) { return ys (d.SMA); });
svg.append ("path")
.attr ("d", line_sma (data))
.attr ("stroke", "white")
.attr ("fill", "none");
var line_ema = d3.svg.line ().x (function (d, i) { return xs (d.YDay); })
.y (function (d, i) { return ys (d.EMA); });
svg.append ("path")
.attr ("d", line_ema (data))
.attr ("stroke", "orange")
.attr ("fill", "none");
}
function type (d)
{
d.Time = +d.Time;
d.Open = +d.Open;
d.High = +d.High;
d.Low = +d.Low;
d.Close = +d.Close;
d.SMA = +d.SMA;
d.EMA = +d.EMA;
return d;
}
$( document ).ready (load);
var data = [
{
"Date":"02/01/2010",
"Time":1265004000,
"YDay":31,
"Open":1.571500,
"High":1.582100,
"Low":1.558700,
"Close":1.580500,
"SMA":1.5663,
"EMA":1.56743786563595
},
{
"Date":"02/02/2010",
"Time":1265090400,
"YDay":32,
"Open":1.580500,
"High":1.586100,
"Low":1.572600,
"Close":1.576900,
"SMA":1.56758,
"EMA":1.56933029250876
},
{
"Date":"02/03/2010",
"Time":1265176800,
"YDay":33,
"Open":1.576700,
"High":1.581900,
"Low":1.571300,
"Close":1.576900,
"SMA":1.56832,
"EMA":1.57084423400701
},
{
"Date":"02/04/2010",
"Time":1265263200,
"YDay":34,
"Open":1.576900,
"High":1.595000,
"Low":1.571000,
"Close":1.580600,
"SMA":1.57017,
"EMA":1.5727953872056
},
{
"Date":"02/05/2010",
"Time":1265349600,
"YDay":35,
"Open":1.580300,
"High":1.586300,
"Low":1.573100,
"Close":1.574500,
"SMA":1.57075,
"EMA":1.57313630976448
},
{
"Date":"02/07/2010",
"Time":1265522400,
"YDay":37,
"Open":1.574300,
"High":1.576800,
"Low":1.571400,
"Close":1.575100,
"SMA":1.57224,
"EMA":1.57352904781159
},
{
"Date":"02/08/2010",
"Time":1265608800,
"YDay":38,
"Open":1.575000,
"High":1.583500,
"Low":1.572700,
"Close":1.577500,
"SMA":1.5745,
"EMA":1.57432323824927
},
{
"Date":"02/09/2010",
"Time":1265695200,
"YDay":39,
"Open":1.577400,
"High":1.578200,
"Low":1.567100,
"Close":1.571400,
"SMA":1.5753,
"EMA":1.57373859059942
},
{
"Date":"02/10/2010",
"Time":1265781600,
"YDay":40,
"Open":1.571300,
"High":1.573800,
"Low":1.549800,
"Close":1.551600,
"SMA":1.57365,
"EMA":1.56931087247953
},
{
"Date":"02/11/2010",
"Time":1265868000,
"YDay":41,
"Open":1.551700,
"High":1.552800,
"Low":1.533500,
"Close":1.537500,
"SMA":1.57025,
"EMA":1.56294869798363
},
{
"Date":"02/12/2010",
"Time":1265954400,
"YDay":42,
"Open":1.537500,
"High":1.545000,
"Low":1.526900,
"Close":1.535200,
"SMA":1.56572,
"EMA":1.5573989583869
},
{
"Date":"02/14/2010",
"Time":1266127200,
"YDay":44,
"Open":1.533200,
"High":1.536100,
"Low":1.531800,
"Close":1.533100,
"SMA":1.56134,
"EMA":1.55253916670952
},
{
"Date":"02/15/2010",
"Time":1266213600,
"YDay":45,
"Open":1.533200,
"High":1.535300,
"Low":1.525500,
"Close":1.526500,
"SMA":1.5563,
"EMA":1.54733133336762
},
{
"Date":"02/16/2010",
"Time":1266300000,
"YDay":46,
"Open":1.526500,
"High":1.529100,
"Low":1.519400,
"Close":1.528000,
"SMA":1.55104,
"EMA":1.54346506669409
},
{
"Date":"02/17/2010",
"Time":1266386400,
"YDay":47,
"Open":1.527900,
"High":1.528400,
"Low":1.511300,
"Close":1.515100,
"SMA":1.5451,
"EMA":1.53779205335528
},
{
"Date":"02/18/2010",
"Time":1266472800,
"YDay":48,
"Open":1.515000,
"High":1.516100,
"Low":1.506300,
"Close":1.513100,
"SMA":1.5389,
"EMA":1.53285364268422
},
{
"Date":"02/19/2010",
"Time":1266559200,
"YDay":49,
"Open":1.513100,
"High":1.516900,
"Low":1.509500,
"Close":1.514100,
"SMA":1.53256,
"EMA":1.52910291414738
},
{
"Date":"02/21/2010",
"Time":1266732000,
"YDay":51,
"Open":1.512400,
"High":1.514200,
"Low":1.510900,
"Close":1.513200,
"SMA":1.52674,
"EMA":1.5259223313179
},
{
"Date":"02/22/2010",
"Time":1266818400,
"YDay":52,
"Open":1.513200,
"High":1.515600,
"Low":1.508800,
"Close":1.511900,
"SMA":1.52277,
"EMA":1.52311786505432
},
{
"Date":"02/23/2010",
"Time":1266904800,
"YDay":53,
"Open":1.511700,
"High":1.522000,
"Low":1.504000,
"Close":1.515900,
"SMA":1.52061,
"EMA":1.52167429204346
},
{
"Date":"02/24/2010",
"Time":1266991200,
"YDay":54,
"Open":1.515900,
"High":1.526600,
"Low":1.511100,
"Close":1.516400,
"SMA":1.51873,
"EMA":1.52061943363477
},
{
"Date":"02/25/2010",
"Time":1267077600,
"YDay":55,
"Open":1.516200,
"High":1.532300,
"Low":1.514400,
"Close":1.526500,
"SMA":1.51807,
"EMA":1.52179554690781
},
{
"Date":"02/26/2010",
"Time":1267164000,
"YDay":56,
"Open":1.526400,
"High":1.529200,
"Low":1.517800,
"Close":1.522200,
"SMA":1.51764,
"EMA":1.52187643752625
},
{
"Date":"02/28/2010",
"Time":1267336800,
"YDay":58,
"Open":1.519800,
"High":1.520400,
"Low":1.514200,
"Close":1.515600,
"SMA":1.5164,
"EMA":1.520621150021
}
];
</script>
<body>
</body>
</html>
Oleg, was correct I can use Time Scales, I had to convert my times from seconds to miliseconds and it work. I now have a different problem with axis but I will start another question rather then chain on this one.
Thank you Oleg the part I was missing when I tried it was the milliseconds versus seconds.

how to do a simple looping carousel of svg:g elements with D3.js

I am trying to make a scrollable list that is part of a bigger d3.js generated chart. clicking on list item acivates changes in the rest of the chart.
How would you make a simple animated looping carousel of svg:g elements with D3.js using the reusable chart pattern ?
Wired to jQuery-mousewheel or some forward and back buttons for example.
http://jsbin.com/igopix/2/edit
NameLoop = function () {
var scroll = 0;
function chart(selection) {
selection.each(function (data) {
var len = data.length,
scroll = scroll % len + len; // keep scroll between 0 <-> len
var nameNodes = d3.select(this).selectAll("g")
.data(data, function(d){return d.ID;} );
var nameNodesEnter = nameNodes.enter().append("g")
.attr("class", "nameNode")
.attr("transform", function(d, i) {
// implement scroll here ??
return "translate(30," + (i+1)*20 + ")";
})
.append("text")
.text(function(d){return d.ID;});
});
return chart;
}
chart.scroll = function(_) {
if (!arguments.length) return scroll;
scroll = _;
return chart;
};
return chart;
};
data = [{ID:"A"},{ID:"B"},{ID:"C"},{ID:"D"},{ID:"E"}];
var svg = d3.select("body").append("svg")
.attr("width", 200)
.attr("height",200)
.attr("id","svg");
var nameloop = NameLoop();
d3.select("#svg").datum(data).call(nameloop);
var body = d3.select("body").append("div");
body.append("span").on("click",function(d){ scroll(1) }).html("forward ");
body.append("span").on("click",function(d){ scroll(-1) }).html("back");

Categories