Related
I have made a violin plot in D3.js with the following code:
<script src="https://d3js.org/d3.v4.js"></script>`
<div id="power"></div>
<script>
var margin = {top: 120, right: 100, bottom: 80, left: 100},
width = 2600 - margin.left - margin.right,
height = 620 - margin.top - margin.bottom;
var svg = d3.select("#power")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Read the data and compute summary statistics for each
d3.csv("static/csv/violinsummary.csv", function (data) {
// Show the X scale
var x = d3.scaleBand()
.range([0, width])
.domain(["2017-09", "2017-10", "2018-02", "2018-03"])
.paddingInner(0)
.paddingOuter(.5);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Show the Y scale
var y = d3.scaleLinear()
.domain([80, 105])
.range([height, 0]);
svg.append("g").call(d3.axisLeft(y));
// Features of density estimate
var kde = kernelDensityEstimator(kernelEpanechnikov(.2), y.ticks(50));
// Compute the binning for each group of the dataset
var sumstat = d3.nest()
.key(function (d) {
return d.DATE;
})
.rollup(function (d) { // For each key..
input = d.map(function (g) {
return g.Power;
});
density = kde(input); // And compute the binning on it.
return (density);
})
.entries(data);
var maxNum = 0;
for (i in sumstat) {
allBins = sumstat[i].value;
kdeValues = allBins.map(function (a) {
return a[1]
});
biggest = d3.max(kdeValues);
if (biggest > maxNum) {
maxNum = biggest
}
}
// The maximum width of a violin must be x.bandwidth = the width dedicated to a group
var xNum = d3.scaleLinear()
.range([0, x.bandwidth()])
.domain([-maxNum, maxNum]);
svg
.selectAll("myViolin")
.data(sumstat)
.enter() // So now we are working group per group
.append("g")
.attr("transform", function (d) {
return ("translate(" + x(d.key) + " ,0)")
}) // Translation on the right to be at the group position
.append("path")
.datum(function (d) {
return (d.value)
}) // So now we are working density per density
.style("opacity", .7)
.style("fill", "#317fc8")
.attr("d", d3.area()
.x0(function (d) {
return (xNum(-d[1]))
})
.x1(function (d) {
return (xNum(d[1]))
})
.y(function (d) {
return (y(d[0]))
})
.curve(d3.curveCatmullRom));
});
function kernelDensityEstimator(kernel, X) {
return function (V) {
return X.map(function (x) {
return [x, d3.mean(V, function (v) {
return kernel(x - v);
})];
});
}
}
function kernelEpanechnikov(k) {
return function (v) {
return Math.abs(v /= k) <= 1 ? 0.75 * (1 - v * v) / k : 0;
};
}
</script>
Data (violinsummary.csv):
Power,DATE
89.29,2017-09
89.9,2017-09
91.69,2017-09
89.23,2017-09
91.54,2017-09
88.49,2017-09
89.15,2017-09
90.85,2017-09
89.59,2017-09
93.38,2017-10
92.41,2017-10
90.65,2017-10
91.07,2017-10
90.13,2017-10
91.73,2017-10
91.09,2017-10
93.21,2017-10
91.62,2017-10
89.58,2017-10
90.59,2017-10
92.57,2017-10
89.99,2017-10
90.59,2017-10
88.12,2017-10
91.3,2017-10
89.59,2018-02
91.9,2018-02
87.83,2018-02
90.36,2018-02
91.38,2018-02
91.56,2018-02
91.89,2018-02
90.95,2018-02
90.15,2018-02
90.24,2018-02
94.04,2018-02
85.4,2018-02
88.47,2018-02
92.3,2018-02
92.46,2018-02
92.26,2018-02
88.78,2018-02
90.13,2018-03
89.95,2018-03
92.98,2018-03
91.94,2018-03
90.29,2018-03
91.2,2018-03
94.22,2018-03
90.71,2018-03
93.03,2018-03
91.89,2018-03
I am trying to make a tooltip for each violin that shows the median and mean upon hover. I cannot figure out how to make the tooltip show up.
I know I need to do something like this with mouseover and mouseout but I'm not sure...
var tooltip = d3.select('#power')
.append('div')
.attr('class', 'tooltip')
.style("opacity", 0);
Any tips/guidance would be very appreciated.
You can implement the tooltip functionality by following two steps.
Step 1:
Initialize the tooltip container which already you did I guess.
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
Step 2:
Change the visibility property of the tooltip in the mouseover, mouseout event of the element. In your case, it's myViolin
.on("mouseover", function() {
tooltip.style("display", null);
})
.on("mouseout", function() {
tooltip.style("display", "none");
})
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
Here is the implementation of tooltip jsFiddle
Hope it helps :)
I am a beginner in JS and D3. Doing reseach for my project I found this library which gives the type of plot needed for our project written in d3js. The input is cvs, but the setup requires using json. To migrate the code I have used d3.json, but the app only loads one data element. Any ideas what's going wrong?
input json (snippet)
[
{"date":"2017-09-03 19:49:51","volume":"3070.33","price":"0.0314009","average":"0.0314009"},
{"date":"2017-09-03 19:59:47","volume":"3061.02","price":"0.0313057","average":"0.0313057"},
{"date":"2017-09-03 20:09:46","volume":"3062.63","price":"0.0313221","average":"0.0313221"},
{"date":"2017-09-03 20:19:54","volume":"3049.41","price":"0.0311875","average":"0.0311875"}
]
Code
/* global d3, _ */
(function() {
var margin = {top: 30, right: 20, bottom: 100, left: 50},
margin2 = {top: 210, right: 20, bottom: 20, left: 50},
width = 764 - margin.left - margin.right,
height = 283 - margin.top - margin.bottom,
height2 = 283 - margin2.top - margin2.bottom;
var parseDate = d3.time.format('%d/%m/%Y %H:%M').parse,
bisectDate = d3.bisector(function(d) { return d.date; }).left,
legendFormat = d3.time.format('%b %d, %Y %H:%M');
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y1 = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]),
y3 = d3.scale.linear().range([60, 0]);
var xAxis = d3.svg.axis().scale(x).orient('bottom'),
xAxis2 = d3.svg.axis().scale(x2).orient('bottom'),
yAxis = d3.svg.axis().scale(y).orient('left');
var priceLine = d3.svg.line()
.interpolate('monotone')
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.price); });
var avgLine = d3.svg.line()
.interpolate('monotone')
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.average); });
var area2 = d3.svg.area()
.interpolate('monotone')
.x(function(d) { return x2(d.date); })
.y0(height2)
.y1(function(d) { return y2(d.price); });
var svg = d3.select('body').append('svg')
.attr('class', 'chart')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom + 60);
svg.append('defs').append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', width)
.attr('height', height);
var make_y_axis = function () {
return d3.svg.axis()
.scale(y)
.orient('left')
.ticks(3);
};
var focus = svg.append('g')
.attr('class', 'focus')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var barsGroup = svg.append('g')
.attr('class', 'volume')
.attr('clip-path', 'url(#clip)')
.attr('transform', 'translate(' + margin.left + ',' + (margin.top + 60 + 20) + ')');
var context = svg.append('g')
.attr('class', 'context')
.attr('transform', 'translate(' + margin2.left + ',' + (margin2.top + 60) + ')');
var legend = svg.append('g')
.attr('class', 'chart__legend')
.attr('width', width)
.attr('height', 30)
.attr('transform', 'translate(' + margin2.left + ', 10)');
legend.append('text')
.attr('class', 'chart__symbol')
.text('Renos : RNS')
var rangeSelection = legend
.append('g')
.attr('class', 'chart__range-selection')
.attr('transform', 'translate(110, 0)');
d3.json('http://rns.ud.vg/datastockstyle.php', function(err, data) {
data.forEach( function(d) {
date = parseDate(d.date);
price = +d.price;
average = +d.average;
volume = +d.volume;
});
var brush = d3.svg.brush()
.x(x2)
.on('brush', brushed);
var xRange = d3.extent(data.map(function(d) { return d.date; }));
x.domain(xRange);
y.domain(d3.extent(data.map(function(d) { return d.price; })));
y3.domain(d3.extent(data.map(function(d) { return d.price; })));
x2.domain(x.domain());
y2.domain(y.domain());
var min = d3.min(data.map(function(d) { return d.price; }));
var max = d3.max(data.map(function(d) { return d.price; }));
var range = legend.append('text')
.text(legendFormat(new Date(xRange[0])) + ' - ' + legendFormat(new Date(xRange[1])))
.style('text-anchor', 'end')
.attr('transform', 'translate(' + width + ', 0)');
focus.append('g')
.attr('class', 'y chart__grid')
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat(''));
var averageChart = focus.append('path')
.datum(data)
.attr('class', 'chart__line chart__average--focus line')
.attr('d', avgLine);
var priceChart = focus.append('path')
.datum(data)
.attr('class', 'chart__line chart__price--focus line')
.attr('d', priceLine);
focus.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0 ,' + height + ')')
.call(xAxis);
focus.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(12, 0)')
.call(yAxis);
var focusGraph = barsGroup.selectAll('rect')
.data(data)
.enter().append('rect')
.attr('class', 'chart__bars')
.attr('x', function(d, i) { return x(d.date); })
.attr('y', function(d) { return 155 - y3(d.price); })
.attr('width', 1)
.attr('height', function(d) { return y3(d.price); });
var helper = focus.append('g')
.attr('class', 'chart__helper')
.style('text-anchor', 'end')
.attr('transform', 'translate(' + width + ', 0)');
var helperText = helper.append('text')
var priceTooltip = focus.append('g')
.attr('class', 'chart__tooltip--price')
.append('circle')
.style('display', 'none')
.attr('r', 2.5);
var averageTooltip = focus.append('g')
.attr('class', 'chart__tooltip--average')
.append('circle')
.style('display', 'none')
.attr('r', 2.5);
var mouseArea = svg.append('g')
.attr('class', 'chart__mouse')
.append('rect')
.attr('class', 'chart__overlay')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.on('mouseover', function() {
helper.style('display', null);
priceTooltip.style('display', null);
averageTooltip.style('display', null);
})
.on('mouseout', function() {
helper.style('display', 'none');
priceTooltip.style('display', 'none');
averageTooltip.style('display', 'none');
})
.on('mousemove', mousemove);
context.append('path')
.datum(data)
.attr('class', 'chart__area area')
.attr('d', area2);
context.append('g')
.attr('class', 'x axis chart__axis--context')
.attr('y', 0)
.attr('transform', 'translate(0,' + (height2 - 22) + ')')
.call(xAxis2);
context.append('g')
.attr('class', 'x brush')
.call(brush)
.selectAll('rect')
.attr('y', -6)
.attr('height', height2 + 7);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]);
var i = bisectDate(data, x0, 1);
var d0 = data[i - 1];
var d1 = data[i];
var d = x0 - d0.date > d1.date - x0 ? d1 : d0;
helperText.text(legendFormat(new Date(d.date)) + ' - Price: ' + d.price + ' Avg: ' + d.average);
priceTooltip.attr('transform', 'translate(' + x(d.date) + ',' + y(d.price) + ')');
averageTooltip.attr('transform', 'translate(' + x(d.date) + ',' + y(d.average) + ')');
}
function brushed() {
var ext = brush.extent();
if (!brush.empty()) {
x.domain(brush.empty() ? x2.domain() : brush.extent());
y.domain([
d3.min(data.map(function(d) { return (d.date >= ext[0] && d.date <= ext[1]) ? d.price : max; })),
d3.max(data.map(function(d) { return (d.date >= ext[0] && d.date <= ext[1]) ? d.price : min; }))
]);
range.text(legendFormat(new Date(ext[0])) + ' - ' + legendFormat(new Date(ext[1])))
focusGraph.attr('x', function(d, i) { return x(d.date); });
var days = Math.ceil((ext[1] - ext[0]) / (24 * 3600 * 1000))
focusGraph.attr('width', (40 > days) ? (40 - days) * 5 / 6 : 5)
}
priceChart.attr('d', priceLine);
averageChart.attr('d', avgLine);
focus.select('.x.axis').call(xAxis);
focus.select('.y.axis').call(yAxis);
}
var dateRange = ['1h', '1d', '1w', '1m', '3m', '6m', '1y']
for (var i = 0, l = dateRange.length; i < l; i ++) {
var v = dateRange[i];
rangeSelection
.append('text')
.attr('class', 'chart__range-selection')
.text(v)
.attr('transform', 'translate(' + (18 * i) + ', 0)')
.on('click', function(d) { focusOnRange(this.textContent); });
}
function focusOnRange(range) {
var today = new Date(data[data.length - 1].date)
var ext = new Date(data[data.length - 1].date)
if (range === '1m')
ext.setMonth(ext.getMonth() - 1)
if (range === '1w')
ext.setDate(ext.getDate() - 7)
if (range === '1d')
ext.setDate(ext.getDate() - 1)
if (range === '1h')
ext.setTime(ext.getTime() - 60*60*1000)
if (range === '3m')
ext.setMonth(ext.getMonth() - 3)
if (range === '6m')
ext.setMonth(ext.getMonth() - 6)
if (range === '1y')
ext.setFullYear(ext.getFullYear() - 1)
brush.extent([ext, today])
brushed()
document.getElementById("demo").innerHTML =ext;
context.select('g.x.brush').call(brush.extent([ext, today]))
}
})// end Data
}());
The data contains hyphens not forward slashes.
Your date parsing should not be
var parseDate = d3.time.format('%d/%m/%Y %H:%M').parse,
it should be,
var parseDate = d3.time.format('%d-%m-%Y %H:%M').parse,
Also, what is y0? I don't see y0 in your scales.
.y0(height2)
I don't know what's wrong with your code, but others will be able to help easier with this,
jsFiddle
Below is the code for horizontal stacked Bar Chart. When I hover on any bar, it shows me content of the element all in single line. I want to add line break to separate each parameter of the data. I tried looking for this type of question and found something with 'tspan' but that is not working in my code. Please have a look.
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%# taglib prefix="lit" uri="/WEB-INF/......" %>
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<link rel="stylesheet" media="all" type="text/css" href="<c:url value="/styl........."/>" />
</head>
<body>
<script src="//d3js.org/d3.v3.js"></script>
<script>
var i = 0;
var margins = {
top: 12,
left: 48,
right: 24,
bottom: 24
},
legendPanel = {
width: 180
},
width = 1500 - margins.left - margins.right - legendPanel.width,
height = 900 - margins.top - margins.bottom,
dataset = ${JSONData},
series = dataset.map(function (d) {
return d.name;
}),
dataset = dataset.map(function (d) {
return d.data.map(function (o) {
return {
name: d.name,
y: +o.count,
x: o.time
};
});
}),
stack = d3.layout.stack();
stack(dataset);
var dataset = dataset.map(function (group) {
return group.map(function (d) {
return {
name: d.name,
x: d.y,
y: d.x,
x0: d.y0
};
});
}),
svg = d3.select('body')
.append('svg')
.attr('width', width + margins.left + margins.right + legendPanel.width)
.attr('height', height + margins.top + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')'),
xMax = d3.max(dataset, function (group) {
return d3.max(group, function (d) {
return d.x + d.x0;
});
}),
xScale = d3.scale.linear()
.domain([0, xMax])
.range([0, width]),
months = dataset[0].map(function (d) {
return d.y;
}),
_ = console.log(months),
yScale = d3.scale.ordinal()
.domain(months)
.rangeRoundBands([0, height], .1),
xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom'),
yAxis = d3.svg.axis()
.scale(yScale)
.orient('left'),
colours = d3.scale.category10(),
groups = svg.selectAll('g')
.data(dataset)
.enter()
.append('g')
.style('fill', function (d, i) {
return colours(i);
}),
rects = groups.selectAll('rect')
.data(function (d) {
return d;
})
.enter()
.append('rect')
.attr('x', function (d) {
return xScale(d.x0);
})
.attr('y', function (d, i) {
return yScale(d.y);
})
.attr('height', function (d) {
return yScale.rangeBand();
})
.attr('width', function (d) {
return xScale(d.x);
})
.on('mouseover', function (d) {
var xPos = parseFloat(d3.select(this).attr('x')) / 2 + width/2;
var yPos = parseFloat(d3.select(this).attr('y')) + yScale.rangeBand() / 2;
d3.select('#tooltip')
.style('left', xPos + 'px')
.style('top', yPos + 'px')
.select('#value')
.text("Count: " + d.x + "Time: " + d.y + "String1"+
"\nString2");
//.text(d.x+","+d.y+","+d.name);
d3.select('#tooltip').classed('hidden', false);
})
.on('mouseout', function () {
d3.select('#tooltip').classed('hidden', true);
})
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'axis')
.call(yAxis);
svg.append('rect')
.attr('fill', 'yellow')
.attr('width', 160)
.attr('height', 30 * dataset.length)
.attr('x', width + margins.left)
.attr('y', 0);
series.forEach(function (s, i) {
svg.append('text')
.attr('fill', 'black')
.attr('x', width + margins.left + 8)
.attr('y', i * 24 + 24)
.text(s);
svg.append('rect')
.attr('fill', colours(i))
.attr('width', 60)
.attr('height', 20)
.attr('x', width + margins.left + 90)
.attr('y', i * 24 + 6);
});
</script>
<div id="tooltip" class="hidden">
<p><span id="value">100</span>
</p>
</div>
</body>
</html>
Your "tooltip" is just an HTML span; so all you need to do is use .html and a <br> for the line break:
d3.select('#tooltip')
.style('left', xPos + 'px')
.style('top', yPos + 'px')
.select('#value')
.html("Count: " + d.x + "Time: " + d.y + " String1" + "<br>String2");
If you had been creating an svg text element, then you'd need to append multiple tspan elements to it.
Full code:
<html>
<head>
</head>
<body>
<script src="//d3js.org/d3.v3.js"></script>
<script>
var i = 0;
var margins = {
top: 12,
left: 48,
right: 24,
bottom: 24
},
legendPanel = {
width: 180
},
width = 1500 - margins.left - margins.right - legendPanel.width,
height = 900 - margins.top - margins.bottom,
dataset = [{data:[{
count: Math.random(),
time: "One"
},{
count: Math.random(),
time: "Two"
},{
count: Math.random(),
time: "Three"
}]}];
series = dataset.map(function(d) {
return d.name;
}),
dataset = dataset.map(function(d) {
return d.data.map(function(o) {
return {
name: d.name,
y: +o.count,
x: o.time
};
});
}),
stack = d3.layout.stack();
stack(dataset);
var dataset = dataset.map(function(group) {
return group.map(function(d) {
return {
name: d.name,
x: d.y,
y: d.x,
x0: d.y0
};
});
}),
svg = d3.select('body')
.append('svg')
.attr('width', width + margins.left + margins.right + legendPanel.width)
.attr('height', height + margins.top + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')'),
xMax = d3.max(dataset, function(group) {
return d3.max(group, function(d) {
return d.x + d.x0;
});
}),
xScale = d3.scale.linear()
.domain([0, xMax])
.range([0, width]),
months = dataset[0].map(function(d) {
return d.y;
}),
_ = console.log(months),
yScale = d3.scale.ordinal()
.domain(months)
.rangeRoundBands([0, height], .1),
xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom'),
yAxis = d3.svg.axis()
.scale(yScale)
.orient('left'),
colours = d3.scale.category10(),
groups = svg.selectAll('g')
.data(dataset)
.enter()
.append('g')
.style('fill', function(d, i) {
return colours(i);
}),
rects = groups.selectAll('rect')
.data(function(d) {
return d;
})
.enter()
.append('rect')
.attr('x', function(d) {
return xScale(d.x0);
})
.attr('y', function(d, i) {
return yScale(d.y);
})
.attr('height', function(d) {
return yScale.rangeBand();
})
.attr('width', function(d) {
return xScale(d.x);
})
.on('mouseover', function(d) {
var xPos = parseFloat(d3.select(this).attr('x')) / 2 + width / 2;
var yPos = parseFloat(d3.select(this).attr('y')) + yScale.rangeBand() / 2;
d3.select('#tooltip')
.style('left', xPos + 'px')
.style('top', yPos + 'px')
.select('#value')
.html("Count: " + d.x + "Time: " + d.y + " String1" + "<br>String2");
d3.select('#tooltip').classed('hidden', false);
})
.on('mouseout', function() {
d3.select('#tooltip').classed('hidden', true);
})
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'axis')
.call(yAxis);
svg.append('rect')
.attr('fill', 'yellow')
.attr('width', 160)
.attr('height', 30 * dataset.length)
.attr('x', width + margins.left)
.attr('y', 0);
series.forEach(function(s, i) {
svg.append('text')
.attr('fill', 'black')
.attr('x', width + margins.left + 8)
.attr('y', i * 24 + 24)
.text(s);
svg.append('rect')
.attr('fill', colours(i))
.attr('width', 60)
.attr('height', 20)
.attr('x', width + margins.left + 90)
.attr('y', i * 24 + 6);
});
</script>
<div id="tooltip" class="hidden">
<p>
<span id="value">100</span>
</p>
</div>
</body>
</html>
Code
In this file http://tc51.net/w/at/stackoverflow.com/20150604.1/20150604.1.zip
You will find all .htm, .js, .css, .csv files of:
The example I started with
My current work in progess.
The problem
Since I did some refactoring required for further developments, the chart drag/zoom feature is not working anymore. No apparent JavaScript error in debugger, and it seems to have to do with 3Djs inner zoom functions so I have no clue what I did wrong. Can you help me find out? Thank you.
(I tried to create two JSFiddles as well but they don't work at all, I have no idea why: Start example:
https://jsfiddle.net/TTTT/erndok36/2 Current work in progress: https://jsfiddle.net/TTTT/mfudwy0q
)
And here is some code:
//Start example: http://mbostock.github.io/d3/talk/20111018/area-gradient.html
function parseDate(unix_timestamp){return new Date(unix_timestamp*1000);}
var svg, m, w, h, x, y, xAxis, yAxis, area, line, gradient, margin, varData,
//parseDate = d3.time.format('%Y-%m-%d').parse,
format = d3.time.format('%Y')
function CreateSvg()
{
margin = {top: 79, right: 80, bottom: 160, left: 79};
//m = [79, 80, 160, 79];
w = 1280 - margin.right - margin.left;
h = 800 - margin.top - margin.bottom;
//Scales. Note the inverted domain for the y-scale: bigger is up!
x = d3.time.scale().range([0, w]);
y = d3.scale.linear().range([h, 0]);
xAxis = d3.svg.axis().scale(x).orient('bottom').tickPadding(6).tickFormat(d3.time.format('%Y/%m/%d %H:%M:%S')).ticks(30); //.tickSize(-h, 0)
yAxis = d3.svg.axis().scale(y).orient('left').tickSize(w).tickPadding(6);
area = d3.svg.area()
.interpolate('step-after')
.x(function(d) { return x(d.date); })
.y0(y(0))
.y1(function(d) { return y(d.value); });
line = d3.svg.line()
.interpolate('step-after')
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); });
svg = d3.select('#ChartContainer').append('svg:svg')
.attr('width', w + margin.right + margin.left)
.attr('height', h + margin.top + margin.bottom)
.append('svg:g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
gradient = svg.append('svg:defs').append('svg:linearGradient')
.attr('id', 'gradient')
.attr('x2', '0%')
.attr('y2', '100%');
gradient.append('svg:stop')
.attr('offset', '40%')
.attr('stop-color', '#f00')
.attr('stop-opacity', .5);
gradient.append('svg:stop')
.attr('offset', '100%')
.attr('stop-color', '#0f0')
.attr('stop-opacity', 1);
svg.append('svg:clipPath')
.attr('id', 'clip')
.append('svg:rect')
.attr('x', x(0))
.attr('y', y(1))
.attr('width', x(1) - x(0))
.attr('height', y(0) - y(1));
svg.append('svg:g')
.attr('class', 'y axis')
.attr('transform', 'translate(' + w + ',0)');
svg.append('svg:path')
.attr('class', 'area')
.attr('clip-path', 'url(#clip)')
.style('fill', 'url(#gradient)');
svg.append('svg:g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + h + ')');
svg.append('svg:path')
.attr('class', 'line')
.attr('clip-path', 'url(#clip)');
svg.append('svg:rect')
.attr('class', 'pane')
.attr('width', w)
.attr('height', h)
.call(d3.behavior.zoom().on('zoom', zoom));
svg.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 0-margin.left)
.attr('x', 0-(h/2))
.attr('dy', '1em')
.style('text-anchor', 'middle')
.text('Noise (dB)');
svg.append('text')
.attr('y', w/2)
.attr('x', h)
.attr('dx', '1em')
.style('text-anchor', 'middle')
.text('Time');
}
function CreateData()
{
//.csv() function is async!
d3.csv('data-noise-example.csv', function(data)
{
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
//Adds two random data. ''Getting started experimentation code.'
//data.reverse();
data.push({date:parseDate(1433160989), value:Math.random() * 180});
data.push({date:parseDate(1433160990), value:Math.random() * 180});
data.push({date:parseDate(1433160991), value:179});
data.push({date:parseDate(1433160992), value:1});
//data.reverse();
varData = data;
ReDraw(varData);
});
}
function ReDrawTest()
{
varData.push({date:parseDate(1433160993), value:Math.random() * 180});
varData.push({date:parseDate(1433160994), value:Math.random() * 180});
ReDraw(varData);
}
function FirstDraw()
{
CreateData();
//ReDraw(varData);
}
function ReDraw(data)
{
y.domain([0, 180]);
x.domain([parseDate(1433160660), parseDate(1433160780)]);
//Bind the data to our path elements
svg.select('path.area').data([data]);
svg.select('path.line').data([data]);
var t = svg.select('g.x.axis').call(xAxis).selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em');
t.attr('transform', 'translate(-2,0)').attr('transform', 'rotate(-90)');
svg.select('g.y.axis').call(yAxis);
svg.select('path.area').attr('d', area);
svg.select('path.line').attr('d', line); //.on('click', clickPath).on('mouseover', onMouseOverPath);
//d3.select('#footer span').text('U.S. Commercial Flights, ' + x.domain().map(format).join('-'));
}
/*function clickPath(e){
console.log('onclickPath', this);
//console.log('onclickPath', this.attr("d"));
var coordinates = d3.mouse(this);
console.log(coordinates[0],coordinates[1]);
}
function findData(x,y)
{
var relY = Math.abs(height-y)/height;
var valY = relY*(chartMaxY-chartMinY);
return [valY,''];
}
function onMouseOverPath(e){
var coordinates = d3.mouse(this);
var x = coordinates[0];
var y = coordinates[1];
var coordValue = 'Coord.: x:' + x + ',y:' + y;
$('#coordValue').text(coordValue);
//Probably not the cleanest way, but that way gives result ...
//console.log(svg.select('title'));
//console.log(coordinates[0] + ',' +coordinates[1]);
var retrievedData = findData(x,y);
var noiseValue = 'Noise: '+ retrievedData[0]+','+retrievedData[1];
$('#noiseValue').text(noiseValue);
svg.append('svg:title').text(noiseValue + "\r\n" + coordValue);
svg.select('title').text(noiseValue + "\r\n" + coordValue);
}
function onMouseOutPath(){alert('mouseOutPath');}
function click() {alert('onclick');}
function onMouseOver(){alert('mouseOver');}
function onMouseOut(){alert('mouseOut');}*/
function zoom() {
d3.event.transform(x); //TODO d3.behavior.zoom should support extents
ReDraw(varData);
}
$(document).ready(function() {
CreateSvg();
FirstDraw();
});
Solution found:
Moving
y.domain([0, 180]);
x.domain([parseDate(1433160660), parseDate(1433160780)]);
to
function FirstDraw(){...}
(I thought I had tested it already ...)
I have this D3 radial tree graph and what I need is the images to not being rotated, but appear straight together it's corresponding blue circle. Here is the code working in Codepen and I copy it here:
var radius = 960 / 2;
var tree = d3.layout.tree()
.size([360, radius - 120])
.separation(function (a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });
var diagonal = d3.svg.diagonal.radial()
.projection(function (d) { return [d.y, d.x / 180 * Math.PI]; })
var vis = d3.select('#graph').append('svg:svg')
.attr('width', radius * 2)
.attr('height', radius * 2 - 150)
.append('g')
.attr('transform', 'translate(' + radius + ',' + radius + ')');
d3.json('flare2.json', function (json) {
var nodes = tree.nodes(json);
var link = vis.selectAll('path.link')
.data(tree.links(nodes))
.enter().append('path')
.attr('class', 'link')
.attr('d', diagonal);
var node = vis.selectAll('g.node')
.data(nodes)
.enter().append('g')
.attr('class', 'node')
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; });
node.append('circle')
.attr('r', 4.5);
node.append('image')
.attr('xlink:href', 'img/avatar.40.gif')
.attr('width', 40)
.attr('height', 40)
.attr('x', 10);
node.append('image')
.attr('xlink:href', 'img/avatar.41.gif')
.attr('width', 40)
.attr('height', 40)
.attr('x', 50 );
});
Your nodes are rotated
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
And your images are appended to your nodes
node.append('image')
So you need to rotate the images back
.attr("transform", function(d) { return "rotate(" + (90 - d.x) + ")"; })
I'm not sure exactly how you want to position them, but you need to translate them on both x and y.
See a working example: http://codepen.io/anon/pen/qiCeG