I would like to rotate the axis and the boxes location with text (location rotated but keep box not rotated), currently only the axis has been rotated!
test()
function test() {
var width = 800
var height = 600
var margin = 20
var svg = d3.select('body').append('svg')
.attr('width',width).attr('height',height)
.style('border','1px solid red')
var g = svg.append('g')
.attr("transform", `translate(${margin}, ${margin})`)
var data = [
{
pos:0,
name:'A'
},
{
pos:2,
name:'B'
},
{
pos:12,
name:'C'
},
{
pos:15,
name:'D'
},
{
pos:20,
name:'E'
},
{
pos:23,
name:'F'
},
{
pos:26,
name:'G'
},
];
var xranges = data.map(function(d){
return d.pos
})
var scalelinear = d3.scaleLinear()
.domain([d3.min(xranges), d3.max(xranges)])
.range([0, width-2*margin]);
var scalepoint = d3.scalePoint()
.domain(xranges)
.range([0, width-2*margin]);
var scale = scalepoint
var x_axis = d3.axisTop()
.scale(scale)
g.append("g")
.call(x_axis)
.attr("transform", `translate(0, 5) rotate(30)`)
var iw = 48
var ih = 80
var g1 = g.selectAll(null)
.data(data).enter()
.append('g')
.attr("transform", `translate(0, 5)`)
g1.append('rect')
.attr('x', d => scale(d.pos)-iw/2)
.attr('y', 0)
.attr('width', iw)
.attr('height', ih)
.attr('fill','none')
.attr('stroke','black')
g1.append('text')
.attr('x', d => scale(d.pos))
.attr('y', ih/2)
.text(d => d.name)
.attr('text-anchor','middle')
.attr('fill','none')
.attr('stroke','black')
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
Apply rotate to the top-level group:
test()
function test() {
var width = 800
var height = 600
var margin = 20
var svg = d3.select('body').append('svg')
.attr('width',width).attr('height',height)
.style('border','1px solid red')
var g = svg.append('g')
.attr("transform", `translate(${margin}, ${margin}) rotate(30)`)
var data = [
{
pos:0,
name:'A'
},
{
pos:2,
name:'B'
},
{
pos:12,
name:'C'
},
{
pos:15,
name:'D'
},
{
pos:20,
name:'E'
},
{
pos:23,
name:'F'
},
{
pos:26,
name:'G'
},
];
var xranges = data.map(function(d){
return d.pos
})
var scalelinear = d3.scaleLinear()
.domain([d3.min(xranges), d3.max(xranges)])
.range([0, width-2*margin]);
var scalepoint = d3.scalePoint()
.domain(xranges)
.range([0, width-2*margin]);
var scale = scalepoint
var x_axis = d3.axisTop()
.scale(scale)
g.append("g")
.call(x_axis)
.attr("transform", `translate(0, 5)`)
var iw = 48
var ih = 80
var g1 = g.selectAll(null)
.data(data).enter()
.append('g')
.attr("transform", `translate(0, 5)`)
g1.append('rect')
.attr('x', d => scale(d.pos)-iw/2)
.attr('y', 0)
.attr('width', iw)
.attr('height', ih)
.attr('fill','none')
.attr('stroke','black')
g1.append('text')
.attr('x', d => scale(d.pos))
.attr('y', ih/2)
.text(d => d.name)
.attr('text-anchor','middle')
.attr('fill','none')
.attr('stroke','black')
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
Related
This demo try to update data when click the graph. it use the join method but looks like data and axis have been overlapped after click. the previous drawing not been cleared!
console.clear()
click_to_update()
function click_to_update() {
var svg = d3.select('body').append('svg')
.attr('width',600)
.attr('height',400)
.style('border','5px solid red')
var frame = svg.append('g').attr('class','frame')
frame.append('g').attr('class','xaxis')
frame.append('g').attr('class','yaxis')
d3.select('svg').on('click', function(){
var data = fetch_data()
refresh_graph(data)
})
var data = fetch_data()
refresh_graph(data)
function refresh_graph(data){
var svg = d3.select('svg')
var colors = d3.scaleOrdinal(d3.schemeSet3)
var margin = {left: 40,top: 10, right:10,bottom: 60},
width = +svg.attr('width') - margin.left - margin.right,
height = +svg.attr('height') - margin.top - margin.bottom;
var g = d3.select('.frame')
.attr('transform',`translate(${margin.left},${margin.top})`)
var xrange = data.map(function(d,i) { return i; })
var x = d3.scalePoint()
.domain(xrange)
.range([0, width]);
var ymax = d3.max(data,d => d.value)
var y = d3.scaleLinear()
.domain([0,ymax])
.range([height, 0]);
var drawpath = function(d,i) {
var ax = x(i)
var ay = y(d.value)
var bx = x(i)
var by = y(0)
var path = ['M',ax,ay,'L',bx,by]
return path.join(' ')
}
var g1 = g.selectAll('path')
.data(data)
.join('path')
.attr('stroke','gray')
.attr('stroke-width',1)
.style('fill', (d, i) => colors(i))
.transition()
.attr('d', drawpath)
g.selectAll('circle')
.data(data)
.join('circle')
.attr('fill','red')
.attr('cx',(d,i) => x(i))
.attr('cy',(d,i) => y(d.value))
.attr('r',5)
var xaxis = d3.select('.xaxis')
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
var yaxis = d3.select('.yaxis')
.attr('transform','translate(-5,0)')
.call(d3.axisLeft(y));
}
function fetch_data(){
var num = parseInt(Math.random()*20) + 5
var data = d3.range(num).map(d => {
return {value:Math.random()}
})
return data
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>
I want create a line chart, when I move mouse it'll show the point and this point data. This is my code. But I found the point position isn't correct. I have no idea. Thanks your help.
This is my code. I use d3 v4.
(function lineChart() {
elementId = 'real_monitor_platform_getBetAmount';
xMax = 1528170430000;
yMax = 8;
xMin = 1528170360000;
yMin = 0;
x = 'unixtime';
y = 'betAmount';
dataset = [{unixtime:1528170360000,betAmount:0},
{unixtime:1528170370000,betAmount:1},
{unixtime:1528170380000,betAmount:2},
{unixtime:1528170390000,betAmount:3},
{unixtime:1528170400000,betAmount:5},
{unixtime:1528170410000,betAmount:6},
{unixtime:1528170420000,betAmount:7},
{unixtime:1528170430000,betAmount:8}];
dataset.sort((a, b) => a[x] - b[x]);
const margin = {
top: 30, right: 40, bottom: 120, left: 60,
};
const w = 700;
const h = 300;
const width = w + margin.left + margin.right;
const height = h + margin.top + margin.bottom;
const formatTime = d3.timeFormat('%Y-%m-%d %H:%M:%S');
const svg = d3.select(`#${elementId}`).append('svg')
.attr('width', width)
.attr('height', height),
areaWidth = width - margin.left - margin.right,
areaHeight = svg.attr('height') - margin.top - margin.bottom,
g = svg.append('g')
.attr('id', 'group')
.attr('transform', `translate(${margin.left},${margin.top})`)
.attr('width', areaWidth)
.attr('height', areaHeight);
const xScale = d3.scaleTime()
.domain([xMin, xMax])
.range([0, areaWidth]);
const yScale = d3.scaleLinear().domain([yMin, yMax]).range([areaHeight, 0]);
// create axis objects
const xAxis = d3.axisBottom(xScale).ticks(width / 100);
const yAxis = d3.axisLeft(yScale);
const line = d3.line()
.x(d =>
xScale(d[x]),
).y(d =>
yScale(d[y]),
);
const t = d3.transition()
.duration(500)
.ease(d3.easeLinear);
const xGrooup = g.append('g')
.attr('transform', `translate(0,${areaHeight})`)
.call(xAxis.tickFormat(formatTime).ticks(3));
const yGroup = g.append('g')
.attr('transform', 'translate(0,0)')
.call(yAxis);
g.append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', areaWidth)
.attr('height', areaHeight);
const bisectDate = d3.bisector(d => d.unixtime).left;
const focus = g.append('g')
.attr('class', 'focus')
.style('display', 'none');
const circle = focus.append('circle')
.attr('r', 4)
.style('fill', '#F1F3F3')
.style('stroke', '#6F257F')
.style('stroke-width', '1px');
const updateLine = g.append('g')
.attr('class', 'chart')
.selectAll('line')
.data([dataset]);
const enterLine = updateLine.enter();
const exitLine = updateLine.exit();
const path = enterLine.append('path')
.attr('clip-path', 'url(#clip)')
.attr('class', 'line')
.attr('d', line)
.attr('fill', 'none')
.attr('stroke', 0)
.transition(t)
.attr('stroke-width', 1)
.attr('stroke', 'DodgerBlue');
exitLine.remove();
const zoom = d3.zoom()
.scaleExtent([1, 80])
.translateExtent([[0, 0], [areaWidth, areaHeight]])
.on('zoom', zoomed);
const zoomRect = svg.append('rect')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(0,0)')
.style('fill', 'transparent')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.call(zoom)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
function zoomed() {
const new_xScale = d3.event.transform.rescaleX(xScale);
xGrooup.call(xAxis.scale(new_xScale));
d3.select(`#${elementId}`).select('svg').select('#group').select('.chart')
.select('path.line')
.attr('d', line.x(d => new_xScale(d.unixtime)));
}
function mousemove() {
var transform = d3.zoomTransform(this);
var xt = transform.rescaleX(xScale);
var yt = transform.rescaleY(yScale);
let g = d3.select("#group")._groups[0][0]
var mouse = d3.mouse(g);
var x0 = xt.invert(mouse[0]);
var i = bisectDate(dataset, x0, 1);
var d0 = dataset[i - 1];
var d1 = dataset[i];
var d = x0 - d0[x] > d1[x] - x0 ? d1 : d0;
circle.attr("transform", `translate(${transform.applyX(xScale(d[x]))},${transform.applyY(yScale(d[y]))})`);
}
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="real_monitor_platform_getBetAmount"></div>
edit
This is work for me.
(function lineChart() {
elementId = 'real_monitor_platform_getBetAmount';
xMax = 1528170430000;
yMax = 8;
xMin = 1528170360000;
yMin = 0;
x = 'unixtime';
y = 'betAmount';
dataset = [{unixtime:1528170360000,betAmount:0},
{unixtime:1528170370000,betAmount:1},
{unixtime:1528170380000,betAmount:2},
{unixtime:1528170390000,betAmount:3},
{unixtime:1528170400000,betAmount:5},
{unixtime:1528170410000,betAmount:6},
{unixtime:1528170420000,betAmount:7},
{unixtime:1528170430000,betAmount:8}];
dataset.sort((a, b) => a[x] - b[x]);
const margin = {
top: 30, right: 40, bottom: 120, left: 60,
};
const w = 700;
const h = 300;
const width = w + margin.left + margin.right;
const height = h + margin.top + margin.bottom;
const formatTime = d3.timeFormat('%Y-%m-%d %H:%M:%S');
const svg = d3.select(`#${elementId}`).append('svg')
.attr('width', width)
.attr('height', height),
areaWidth = width - margin.left - margin.right,
areaHeight = svg.attr('height') - margin.top - margin.bottom,
g = svg.append('g')
.attr('id', 'group')
.attr('transform', `translate(${margin.left},${margin.top})`)
.attr('width', areaWidth)
.attr('height', areaHeight);
const xScale = d3.scaleTime()
.domain([xMin, xMax])
.range([0, areaWidth]);
const yScale = d3.scaleLinear().domain([yMin, yMax]).range([areaHeight, 0]);
// create axis objects
const xAxis = d3.axisBottom(xScale).ticks(width / 100);
const yAxis = d3.axisLeft(yScale);
const line = d3.line()
.x(d =>
xScale(d[x]),
).y(d =>
yScale(d[y]),
);
const t = d3.transition()
.duration(500)
.ease(d3.easeLinear);
const xGrooup = g.append('g')
.attr('transform', `translate(0,${areaHeight})`)
.call(xAxis.tickFormat(formatTime).ticks(3));
const yGroup = g.append('g')
.attr('transform', 'translate(0,0)')
.call(yAxis);
g.append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', areaWidth)
.attr('height', areaHeight);
const bisectDate = d3.bisector(d => d.unixtime).left;
const focus = g.append('g')
.attr('class', 'focus')
.style('display', 'none');
const circle = focus.append('circle')
.attr('r', 4)
.style('fill', '#F1F3F3')
.style('stroke', '#6F257F')
.style('stroke-width', '1px');
const updateLine = g.append('g')
.attr('class', 'chart')
.selectAll('line')
.data([dataset]);
const enterLine = updateLine.enter();
const exitLine = updateLine.exit();
const path = enterLine.append('path')
.attr('clip-path', 'url(#clip)')
.attr('class', 'line')
.attr('d', line)
.attr('fill', 'none')
.attr('stroke', 0)
.transition(t)
.attr('stroke-width', 1)
.attr('stroke', 'DodgerBlue');
exitLine.remove();
const zoom = d3.zoom()
.scaleExtent([1, 80])
.translateExtent([[0, 0], [areaWidth, areaHeight]])
.on('zoom', zoomed);
const zoomRect = svg.append('rect')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(0,0)')
.style('fill', 'transparent')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.call(zoom)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
function zoomed() {
const new_xScale = d3.event.transform.rescaleX(xScale);
xGrooup.call(xAxis.scale(new_xScale));
d3.select(`#${elementId}`).select('svg').select('#group').select('.chart')
.select('path.line')
.attr('d', line.x(d => new_xScale(d.unixtime)));
}
function mouseDate(scale) {
var g = d3.select("#group")._groups[0][0]
var x0 = scale.invert(d3.mouse(g)[0]),
i = bisectDate(dataset, x0, 1),
d0 = dataset[i - 1],
d1 = dataset[i],
d = x0 - d0[x] > d1[x] - x0 ? d1 : d0;
return d;
}
function mousemove() {
var transform = d3.zoomTransform(this);
var xt = transform.rescaleX(xScale);
var yt = transform.rescaleY(yScale);
d = mouseDate(xt);
console.log(transform.applyX(xScale(d[x])));
console.log(transform.applyY(yScale(d[y])));
circle.attr("transform", `translate(${transform.applyX(xScale(d[x]))},${(yScale(d[y]))})`);
}
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="real_monitor_platform_getBetAmount"></div>
You have to take the margins into account.
So, instead of...
const focus = svg.append('g')
... it should be:
const focus = g.append('g')
Here is the code with that change:
(function lineChart() {
elementId = 'real_monitor_platform_getBetAmount';
xMax = 1528170430000;
yMax = 8;
xMin = 1528170360000;
yMin = 0;
x = 'unixtime';
y = 'betAmount';
dataset = [{unixtime:1528170360000,betAmount:0},
{unixtime:1528170370000,betAmount:1},
{unixtime:1528170380000,betAmount:2},
{unixtime:1528170390000,betAmount:3},
{unixtime:1528170400000,betAmount:5},
{unixtime:1528170410000,betAmount:6},
{unixtime:1528170420000,betAmount:7},
{unixtime:1528170430000,betAmount:8}];
dataset.sort((a, b) => a[x] - b[x]);
const margin = {
top: 30, right: 40, bottom: 120, left: 60,
};
const w = 700;
const h = 300;
const width = w + margin.left + margin.right;
const height = h + margin.top + margin.bottom;
const formatTime = d3.timeFormat('%Y-%m-%d %H:%M:%S');
const svg = d3.select(`#${elementId}`).append('svg')
.attr('width', width)
.attr('height', height),
areaWidth = width - margin.left - margin.right,
areaHeight = svg.attr('height') - margin.top - margin.bottom,
g = svg.append('g')
.attr('id', 'group')
.attr('transform', `translate(${margin.left},${margin.top})`)
.attr('width', areaWidth)
.attr('height', areaHeight);
const xScale = d3.scaleTime()
.domain([xMin, xMax])
.range([0, areaWidth]);
const yScale = d3.scaleLinear().domain([yMin, yMax]).range([areaHeight, 0]);
// create axis objects
const xAxis = d3.axisBottom(xScale).ticks(width / 100);
const yAxis = d3.axisLeft(yScale);
const line = d3.line()
.x(d =>
xScale(d[x]),
).y(d =>
yScale(d[y]),
);
const t = d3.transition()
.duration(500)
.ease(d3.easeLinear);
const xGrooup = g.append('g')
.attr('transform', `translate(0,${areaHeight})`)
.call(xAxis.tickFormat(formatTime).ticks(3));
const yGroup = g.append('g')
.attr('transform', 'translate(0,0)')
.call(yAxis);
g.append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', areaWidth)
.attr('height', areaHeight);
const bisectDate = d3.bisector(d => d.unixtime).left;
const focus = g.append('g')
.attr('class', 'focus')
.style('display', 'none');
const circle = focus.append('circle')
.attr('r', 4)
.style('fill', '#F1F3F3')
.style('stroke', '#6F257F')
.style('stroke-width', '1px');
const updateLine = g.append('g')
.attr('class', 'chart')
.selectAll('line')
.data([dataset]);
const enterLine = updateLine.enter();
const exitLine = updateLine.exit();
const path = enterLine.append('path')
.attr('clip-path', 'url(#clip)')
.attr('class', 'line')
.attr('d', line)
.attr('fill', 'none')
.attr('stroke', 0)
.transition(t)
.attr('stroke-width', 1)
.attr('stroke', 'DodgerBlue');
exitLine.remove();
const zoom = d3.zoom()
.scaleExtent([1, 80])
.translateExtent([[0, 0], [areaWidth, areaHeight]])
.on('zoom', zoomed);
const zoomRect = svg.append('rect')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(0,0)')
.style('fill', 'transparent')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.call(zoom)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
function zoomed() {
const new_xScale = d3.event.transform.rescaleX(xScale);
xGrooup.call(xAxis.scale(new_xScale));
d3.select(`#${elementId}`).select('svg').select('#group').select('.chart')
.select('path.line')
.attr('d', line.x(d => new_xScale(d.unixtime)));
}
function mousemove() {
var mouse = d3.mouse(this);
var x0 = xScale.invert(mouse[0]);
var i = bisectDate(dataset, x0);
var d0 = dataset[i - 1];
var d1 = dataset[i];
var d = x0 - d0[x] > d1[x] - x0 ? d1 : d0;
circle.attr("transform", `translate(${xScale(d[x])},${(yScale(d[y]))})`);
}
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="real_monitor_platform_getBetAmount"></div>
By the way, this won't work with the zoom... however, that's another issue.
When I try to brush & zoom a portion of the line chart, some parts of the selected area render outside the chart.
Code and behavior reproduction can be found at this jsbin.
Click & drag to select a portion and zoom in, double click to zoom out.
var svg = d3
.select('body')
.append('svg')
.attr('class', 'chart')
.attr('width', 960)
.attr('height', 500);
var margin = {
top: 40,
right: 40,
bottom: 40,
left: 40
};
var width = +svg.attr('width') - margin.left - margin.right;
var height = +svg.attr('height') - margin.top - margin.bottom;
var g = svg
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var timeParser = d3.timeParse('%Y-%m-%d');
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var brush = d3.brush().on('end', brushended);
var idleTimeout;
var idleDelay = 350;
var x0;
var y0;
var xAxis;
var yAxis;
var line = d3
.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.price);
})
.curve(d3.curveNatural);
var start = new Date();
var end = new Date(start.toDateString());
start.setFullYear(end.getFullYear() - 1);
var startStr = start.toISOString().slice(0, 10);
var endStr = end.toISOString().slice(0, 10);
var url = "https://api.coindesk.com/v1/bpi/historical/close.json?start=" + startStr + "&end=" + endStr;
d3.json(url, function(error, response) {
var data = Object.keys(response.bpi).map(function(date) {
return {
date: timeParser(date),
price: response.bpi[date]
};
});
x0 = d3.extent(data, function(d) {
return d.date;
});
y0 = d3.extent(data, function(d) {
return d.price;
});
x.domain(x0);
y.domain(y0);
xAxis = d3.axisBottom(x);
yAxis = d3.axisLeft(y);
g
.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
g
.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
g
.append('path')
.attr('class', 'line')
.datum(data)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('d', line);
svg
.append('g')
.attr('class', 'brush')
.call(brush);
});
function brushended() {
var s = d3.event.selection;
if (!s) {
if (!idleTimeout) {
return (idleTimeout = setTimeout(idled, idleDelay));
}
x.domain(x0);
y.domain(y0);
} else {
x.domain([s[0][0] - 40, s[1][0] - 40].map(x.invert, x));
y.domain([s[1][1] - 40, s[0][1] - 40].map(y.invert, y));
svg.select('.brush').call(brush.move, null);
}
zoom();
}
function idled() {
idleTimeout = null;
}
function zoom() {
var t = svg.transition().duration(750);
svg
.select('.axis--x')
.transition(t)
.call(xAxis);
svg
.select('.axis--y')
.transition(t)
.call(yAxis);
svg
.select('.line')
.transition(t)
.attr('d', line);
}
.chart {
border: 1px solid #bdbdbd;
box-sizing: border-box;
}
<script src="https://unpkg.com/d3#4.12.2/build/d3.min.js"></script>
That's the expected behaviour. The most common way to deal with that is using a <clipPath>.
For instance, in your case:
var clipPath = g.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
Then, in your path:
g.append('path')
//etc...
.attr("clip-path", "url(#clip)");
Here is the updated JSBin: https://jsbin.com/tatuhipevi/1/edit?js,output
And here the updated S.O. snippet:
var svg = d3
.select('body')
.append('svg')
.attr('class', 'chart')
.attr('width', 960)
.attr('height', 500);
var margin = {
top: 40,
right: 40,
bottom: 40,
left: 40
};
var width = +svg.attr('width') - margin.left - margin.right;
var height = +svg.attr('height') - margin.top - margin.bottom;
var g = svg
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var clipPath = g.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var timeParser = d3.timeParse('%Y-%m-%d');
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var brush = d3.brush().on('end', brushended);
var idleTimeout;
var idleDelay = 350;
var x0;
var y0;
var xAxis;
var yAxis;
var line = d3
.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.price);
})
.curve(d3.curveNatural);
var start = new Date();
var end = new Date(start.toDateString());
start.setFullYear(end.getFullYear() - 1);
var startStr = start.toISOString().slice(0, 10);
var endStr = end.toISOString().slice(0, 10);
var url = "https://api.coindesk.com/v1/bpi/historical/close.json?start=" + startStr + "&end=" + endStr;
d3.json(url, function(error, response) {
var data = Object.keys(response.bpi).map(function(date) {
return {
date: timeParser(date),
price: response.bpi[date]
};
});
x0 = d3.extent(data, function(d) {
return d.date;
});
y0 = d3.extent(data, function(d) {
return d.price;
});
x.domain(x0);
y.domain(y0);
xAxis = d3.axisBottom(x);
yAxis = d3.axisLeft(y);
g
.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
g
.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
g
.append('path')
.attr('class', 'line')
.datum(data)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('d', line)
.attr("clip-path", "url(#clip)");
svg
.append('g')
.attr('class', 'brush')
.call(brush);
});
function brushended() {
var s = d3.event.selection;
if (!s) {
if (!idleTimeout) {
return (idleTimeout = setTimeout(idled, idleDelay));
}
x.domain(x0);
y.domain(y0);
} else {
x.domain([s[0][0] - 40, s[1][0] - 40].map(x.invert, x));
y.domain([s[1][1] - 40, s[0][1] - 40].map(y.invert, y));
svg.select('.brush').call(brush.move, null);
}
zoom();
}
function idled() {
idleTimeout = null;
}
function zoom() {
var t = svg.transition().duration(750);
svg
.select('.axis--x')
.transition(t)
.call(xAxis);
svg
.select('.axis--y')
.transition(t)
.call(yAxis);
svg
.select('.line')
.transition(t)
.attr('d', line);
}
.chart {
border: 1px solid #bdbdbd;
box-sizing: border-box;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
Also, it's a good idea using a <clipPath> in the axes as well.
My barchart draws fine when the page first loads.
But choose hour 2 from the drop-down, and it doesn't want to update to hour 2 data, it just keeps displaying hour 1.
FIDDLE
This is my d3 and js:
$('#bar_chart').css('overflow-x','scroll');
var margin = {top: 20, right: 20, bottom: 40, left: 80},
width = 220 - margin.left - margin.right,
height = 233 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1, 1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
var formatComma = d3.format('0,000');
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.ticks(5)
.outerTickSize(0)
.tickFormat(formatComma);
var svg = d3.select('th.chart-here').append('svg')
.attr('viewBox', '0 0 220 233')
.attr('preserveAspectRatio','xMinYMin meet')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left/1.5 + ',' + margin.top/1.5 + ')');
var table_i = 0;
var arr1 =
[
{'hour':1,'car':[{'audi':1377},{'bmw':716},{'ford':3819},{'mazda':67},{'toyota':11580},{'tesla':0}]},
{'hour':2,'car':[{'audi':9000},{'bmw':2000},{'ford':7000},{'mazda':1000},{'toyota':5000},{'tesla':700}]},
];
var hour = arr1[table_i];
var car=hour.car;
var newobj = [];
for(var hourx1=0;hourx1<car.length;hourx1++){
var xx = car[hourx1];
for (var value in xx) {
var chartvar = newobj.push({car:value,miles:xx[value]});
var data = newobj;
}
}
x.domain(data.map(function(d) { return d.car; }));
y.domain([0, d3.max(data, function(d) { return d.miles; })]);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis)
.append('text')
.attr('y', 6)
.attr('dy', '.71em')
.style('text-anchor', 'start');
function changeHour(){
svg.selectAll('.bar')
.data(data)
.enter().append('rect')
.attr('class', 'bar')
.attr('transform','translate(-20)') //move rects closer to Y axis
.attr('x', function(d) { return x(d.car); })
.attr('width', x.rangeBand()*1)
.attr('y', function(d) { return y(d.miles); })
.attr('height', function(d) { return height - y(d.miles); });
xtext = svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(-20,' + height + ')') //move tick text so it aligns with rects
.call(xAxis);
xtext.selectAll('text')
.attr('transform', function(d) {
return 'translate(' + this.getBBox().height*50 + ',' + this.getBBox().height + ')rotate(0)';
});
//code to enable jqm checkbox
$('#checkbox-2a').on('change', function(e){
originalchange(e);
});
$( '#checkbox-2a' ).checkboxradio({
defaults: true
});
var sortTimeout = setTimeout(function() {
$('#checkbox-2a').prop('checked', false).checkboxradio( 'refresh' ).change();
}, 1000);
function originalchange() {
clearTimeout(sortTimeout);
var IsChecked = $('#checkbox-2a').is(':checked');
// Copy-on-write since tweens are evaluated after a delay.
var x0 = x.domain(data.sort(IsChecked
? function(a, b) { return b.miles - a.miles; }
: function(a, b) { return d3.ascending(a.car, b.car); })
.map(function(d) { return d.car; }))
.copy();
svg.selectAll('.bar')
.sort(function(a, b) { return x0(a.car) - x0(b.car); });
var transition = svg.transition().duration(950),
delay = function(d, i) { return i * 50; };
transition.selectAll('.bar')
.delay(delay)
.attr('x', function(d) { return x0(d.car); });
transition.select('.x.axis')
.call(xAxis)
.selectAll('g')
.delay(delay);
};
}
changeHour();
$('select').change(function() { //function to change hourly data
table_i = $(this).val();
var hour = arr1[table_i];
var car=hour.car;
var newobj = [];
for(var hourx1=0;hourx1<car.length;hourx1++){
var xx = car[hourx1];
for (var value in xx) {
var chartvar = newobj.push({car:value,miles:xx[value]});
var data = newobj;
}
}
x.domain(data.map(function(d) { return d.car; }));
y.domain([0, d3.max(data, function(d) { return d.miles; })]);
changeHour();
})
I thought that by updating in the function changeHour I could isolate just the rects and the text that goes with them, and redraw them based on the selected hour's data.
But it just keeps drawing the first hour.
What am I doing wrong?
2 things not working:
firstly "data" needs to be declared without 'var' in the change function at the end. Declaring it with 'var' makes it a local variable to that function, and once you leave that function it's gone. Saying "data = " without the var means you're using the data variable you've declared further up. It's all to do with scope which is something I still struggle with, but basically with 'var' it doesn't work.
var newobj = [];
for(var hourx1=0;hourx1<car.length;hourx1++){
var xx = car[hourx1];
for (var value in xx) {
var chartvar = newobj.push({car:value,miles:xx[value]});
}
}
data = newobj;
Secondly, your changeHour function only looks for new elements as it hangs all its attribute settings on an .enter() selection, changeHour should be like this:
var dataJoin = svg.selectAll('.bar')
.data(data, function(d) { return d.car; });
// possible new elements, fired first time, set non-data dependent attributes
dataJoin
.enter()
.append('rect')
.attr('class', 'bar')
.attr('transform','translate(-20)') //move rects closer to Y axis
// changes to existing elements (now including the newly appended elements from above) which depend on data values (d)
dataJoin
.attr('x', function(d) { return x(d.car); })
.attr('width', x.rangeBand()*1)
.attr('y', function(d) { return y(d.miles); })
.attr('height', function(d) { return height - y(d.miles); });
For completeness there should be a dataJoin.exit().remove() in there as well but its not something that happens in this dataset
I am a complete newbie in d3.js and I was working around with some code but it's failing to create the path. Can someone help me to figure out what I am doing wrong?
Link for jsfiddle: http://jsfiddle.net/p8S7p/154/
var data = [
{
data: [
[
1420815600000,
2
],
[
1420826400000,
2
],
[
1420837200000,
2
],
[
1420848000000,
2
],
],
parameter: "t1"
},
{
data: [
[
1420815600000,
2
],
[
1420826400000,
2
],
[
1420837200000,
2
],
[
1420848000000,
2
],
],
parameter: "t2"
}
];
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function (d) { return x(d[0]); })
.y(function (d) {
return y(d[1]);
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var cities = [];
for (var i = 0; i < data.length; i++) {
var t = {};
t["name"] = data[i].parameter;
t["values"] = [];
for (var j = 0; j < data[i].data.length; j++) {
var m = {};
m["date"] = data[i].data[j][0];
m["temperature"] = data[i].data[j][1];
t["values"].push(m);
}
cities.push(t);
}
x.domain(d3.extent(data[0].data, function (d) {
return d[0];
}));
y.domain([
d3.min(cities, function (c) {
return d3.min(c.values, function (v) {
return v.temperature;
});
}),
d3.max(cities, function (c) {
return d3.max(c.values, function (v) {
return v.temperature;
});
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Temperature (ºF)");
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line")
.attr("d", function (d) {
return line(d.values);
});
Its meant to be dates on x -axis. How should I parse milliseconds dates to D3 time format?
In your code
var line = d3.svg.line()
.interpolate("basis")
.x(function (d) { return x(d[0]); }) //this is incorrect why d[0]
.y(function (d) {
return y(d[1]); //this is incorrect why d[1]
});
should have been
var line = d3.svg.line()
.interpolate("basis")
.x(function (d) { return x(d.date); })
.y(function (d) {
return y(d.temperature);
});
Coz that's how you have parsed your data here
var cities = [];
for (var i = 0; i < data.length; i++) {
var t = {};
t["name"] = data[i].parameter;
t["values"] = [];
for (var j = 0; j < data[i].data.length; j++) {
var m = {};
m["date"] = data[i].data[j][0];//storing this in date
m["temperature"] = data[i].data[j][1];//storing it in temperature
t["values"].push(m);
}
cities.push(t);
}
Working code here
Hope this helps!