D3.time scale keeps returning default value - javascript

I need to use 0:00-23:00 as my x axis label. I have var x = d3.time.scale().range[0, width]; I want to set its domain to hour_data which is an array that stores a list of times, but no matter I try d3.domain(d3.extent(hour_data, function(d){ return d.hour;})) or d3.domain(hour_data.map(function(d){ return d.hour;});, the domain just keeps at the default GMT time which dates back to 1969.
I only need to print the hours as x axis labels.
Excuse me no formatting the code in blocks. I've been bugged by this error for hours and just don't want the hassle to format code blocks on SO. The code is simple anyway.

You can do something like this:
//this is your data json
var data = [{
hour: 0,
value: 10
}, {
hour: 1,
value: 10
}, {
hour: 13,
value: 10
}, {
hour: 14,
value: 20
}, {
hour: 15,
value: 20
}]
var width = 700,
height = 400,
padding = 100;
// create an svg container
var vis = d3.select("body").
append("svg:svg")
.attr("width", width)
.attr("height", height);
// define the x scale (horizontal)
var format1 = d3.time.format("%H");
var xScale = d3.time.scale()
.nice(d3.time.hour)
.domain(d3.extent(data, function(d) {
//conveting the data into date object
return format1.parse(d.hour + "");
}))
.range([padding, width - padding * 2]); // map these the the chart width = total width minus padding at both sides
// define the x axis
var xAxis = d3.svg.axis()
.ticks(d3.time.hour, 1)
.tickFormat(function(d) {
hours = d.getHours();
return hours;
}).orient("bottom")
.scale(xScale);
// draw x axis with labels and move to the bottom of the chart area
vis.append("g")
.attr("class", "axis") // give it a class so it can be used to select only xaxis labels below
.attr("transform", "translate(0," + (height - padding) + ")")
.call(xAxis);
body {
font: 10px sans-serif;
}
.bar rect {
fill: steelblue;
shape-rendering: crispEdges;
}
.bar text {
fill: #fff;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
Hope this helps!

Need to define the timeline as below, full working example snippet
var parseTime = d3.time.format("%H:%M").parse;
var x = d3.time.scale()
.domain([parseTime("00:00"), parseTime("23:00")])
.range([pad, w - pad]);
var w = 960;
var h = 600;
var pad = 80;
var parseTime = d3.time.format("%H:%M").parse;
var data = [{
"Time": "04:20",
"Close": 7
}, {
"Time": "05:20",
"Close": 8
}, {
"Time": "06:20",
"Close": 9
}, {
"Time": "07:20",
"Close": 6
}, {
"Time": "08:20",
"Close": 5
}, {
"Time": "09:20",
"Close": 7
}, {
"Time": "10:20",
"Close": 3
}, {
"Time": "13:20",
"Close": 8
}, {
"Time": "15:20",
"Close": 9
}, {
"Time": "18:20",
"Close": 6
}, {
"Time": "19:20",
"Close": 5
}, {
"Time": "21:20",
"Close": 7
}, {
"Time": "22:20",
"Close": 3
}];
data.forEach(function (d) {
d.Time = parseTime(d.Time);
d.Close = +d.Close;
});
var x = d3.time.scale()
.domain([parseTime("00:00"), parseTime("23:00")])
.range([pad, w - pad]);
var y = d3.scale.linear()
.domain([0,40])
.range([h - pad, pad]);
var canvas = d3.select("body")
.append("svg")
.attr("class", "chart")
.attr("width", w)
.attr("height", h);
var xaxis = d3.svg.axis()
.scale(x)
.orient("bottom") .tickFormat(d3.time.format("%H:%M"));
var yaxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function (d) { return x(d.Time); })
.y(function (d) { return y(d.Close); });
// Add x-axis.
canvas.append("g")
.attr("class", "axis")
.attr("transform","translate(0," + (h - pad) + ")")
.call(xaxis)
// Add y-axis
canvas.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + pad + ", 0)")
.call(yaxis);
canvas.append("path")
.data([data])
.attr("d", line).attr("class", "line");
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
</head>
<body>
</body>
</html>

Related

D3js charts reponsive

I am trying to get responsive d3js charts. I have set the width and height as 100% for the chart have a resize function which should reduce the size . In what I have done so far it just manages to reduce the labels on the x-axis but the line chart remains of the same size.Is the way I am trying to make the responsive the correct way or is their a better way with which any d3js chart(bar/pie/line) could be made responsive.
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
};
var width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right,
height = parseInt(d3.select("#chart").style("height")) - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5).tickFormat(function (d) {
return d.replace('SEASONAL_', '');
});;
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var valueline = d3.svg.line()
.x(function (d) {
return x(d.name);
})
.y(function (d) {
return y(d.count);
});
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 + ")");
// Get the data
var all = [{
"name": "Seasonal Pop",
"code": "SEASONAL_POP",
"children": [{
"name": "SEASONAL_LYQ1",
"code": "SEASONAL_LYQ1",
"count": 1200
}, {
"name": "SEASONAL_LYQ2",
"code": "SEASONAL_LYQ2",
"count": 2000
}, {
"name": "SEASONAL_LYQ3",
"code": "SEASONAL_LYQ3",
"count": 1060
}, {
"name": "SEASONAL_LYQ4",
"code": "SEASONAL_LYQ4",
"count": 2300
}, {
"name": "SEASONAL_CYQ1",
"code": "SEASONAL_CYQ1",
"count": 1300
}, {
"name": "SEASONAL_CYQ2",
"code": "SEASONAL_CYQ2",
"count": 3400
}, {
"name": "SEASONAL_CYQ3",
"code": "SEASONAL_CYQ3",
"count": 4500
}, {
"name": "SEASONAL_CYQ4",
"code": "SEASONAL_CYQ4",
"count": 5500
}]
}];
var data = all[0].children;
data.forEach(function (d) {
// d.name = +d.date ;
d.count = +d.count;
});
// Scale the range of the data
x.domain(data.map(function (d) {
return d.name;
}));
y.domain([0, d3.max(data, function (d) {
return d.count;
})]);
svg.append("path") // Add the valueline path.
.attr("d", valueline(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
// Define responsive behavior
function resize() {
var width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right,
height = parseInt(d3.select("#chart").style("height")) - margin.top - margin.bottom;
// Update the range of the scale with new width/height
x.range([0, width]);
y.range([height, 0]);
// Update the axis and text with the new scale
svg.select('.x.axis')
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select('.y.axis')
.call(yAxis);
// Force D3 to recalculate and update the line
svg.selectAll('.line')
.attr("d", function(d) { return line(d.count); });
// Update the tick marks
xAxis.ticks(Math.max(width/75, 2));
yAxis.ticks(Math.max(height/50, 2));
};
// Call the resize function whenever a resize event occurs
d3.select(window).on('resize', resize);
// Call the resize function
resize();
body {
font: 12px Arial;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path, .axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
#chart {
width: 100%;
height: 100%;
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body><svg id="chart"></svg></body>
Well your code kinda works. Save that you're dealing with two SVGs. The on the page with id="chart" and this one thats appended.
var svg = d3.select("body")
.append("svg")
But its not really a D3 issue its more to do with responsive SVGs. Responsive charts have problems scaling. The X & Y axis can become misrepresented.
One approach is to scale the entire Chart/SVG. Forget about listening to window resize events. Just set a viewBox property on your main svg. And set a static width hight thats too the correct ratio of your chat.
var svg = d3.select("#chart")
.attr("viewBox", "0 0 " + viewWidth + " " + viewHeight)
example
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
};
var width = 500 - margin.left - margin.right,
height = 240 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5).tickFormat(function (d) {
return d.replace('SEASONAL_', '');
});;
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var valueline = d3.svg.line()
.x(function (d) {
return x(d.name);
})
.y(function (d) {
return y(d.count);
});
var viewWidth = width - margin.left - margin.right;
var viewHeight = height + margin.top + margin.bottom;
var svg = d3.select("#chart")
.attr("viewBox", "0 0 " + viewWidth + " " + viewHeight)
// Get the data
var all = [{
"name": "Seasonal Pop",
"code": "SEASONAL_POP",
"children": [{
"name": "SEASONAL_LYQ1",
"code": "SEASONAL_LYQ1",
"count": 1200
}, {
"name": "SEASONAL_LYQ2",
"code": "SEASONAL_LYQ2",
"count": 2000
}, {
"name": "SEASONAL_LYQ3",
"code": "SEASONAL_LYQ3",
"count": 1060
}, {
"name": "SEASONAL_LYQ4",
"code": "SEASONAL_LYQ4",
"count": 2300
}, {
"name": "SEASONAL_CYQ1",
"code": "SEASONAL_CYQ1",
"count": 1300
}, {
"name": "SEASONAL_CYQ2",
"code": "SEASONAL_CYQ2",
"count": 3400
}, {
"name": "SEASONAL_CYQ3",
"code": "SEASONAL_CYQ3",
"count": 4500
}, {
"name": "SEASONAL_CYQ4",
"code": "SEASONAL_CYQ4",
"count": 5500
}]
}];
var data = all[0].children;
data.forEach(function (d) {
// d.name = +d.date ;
d.count = +d.count;
});
// Scale the range of the data
x.domain(data.map(function (d) {
return d.name;
}));
y.domain([0, d3.max(data, function (d) {
return d.count;
})]);
svg.append("path") // Add the valueline path.
.attr("d", valueline(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
body {
font: 12px Arial;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path, .axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
#chart {
width: 100%;
height: 100%;
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body><svg id="chart"></svg></body>

D3.js line chart grid issue

i have created a line chart using d3.js and added x and y grid in the chart. Only y-axis grid lines are coming and not x-axis grid lines.
I want to create grid like shown in the image below and a line at the top of y-axis, so it will look like perfect rectangle.
var data = [{
x: '1-May-12',
y: 5
}, {
x: '30-Apr-12',
y: 28
}, {
x: '27-Apr-12',
y: 58
}, {
x: '26-Apr-12',
y: 88
}, {
x: '25-Apr-12',
y: 8
}, {
x: '24-Apr-12',
y: 48
}, {
x: '23-Apr-12',
y: 28
}, {
x: '20-Apr-12',
y: 68
}, {
x: '19-Apr-12',
y: 8
}, {
x: '18-Apr-12',
y: 58
}, {
x: '17-Apr-12',
y: 5
}, {
x: '16-Apr-12',
y: 80
}, {
x: '13-Apr-12',
y: 38
}];
var margin = {
top: 30,
right: 20,
bottom: 35,
left: 50
},
width = 1200 - (margin.left + margin.right);
height = 360 - 2 * (margin.top + margin.bottom);
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;
var xScale = d3.time.scale().range([0, width]);
var yScale = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(xScale)
.orient("bottom").ticks(0).tickSize(0)
.tickFormat("").outerTickSize(0);
var yAxis = d3.svg.axis().scale(yScale)
.orient("left").tickSize(0).ticks(0)
.tickFormat("");
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("class", "bg-color")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
function make_x_axis() {
return d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(5)
}
// function for the y grid lines
function make_y_axis() {
return d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);
}
svg.append("g")
.attr("class", "grid")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat("")
);
// Draw the y Grid lines
svg.append("g")
.attr("class", "grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat("")
);
xScale.domain(d3.extent(data, function(d) {
return parseDate(d.x);
}));
yScale.domain([0, d3.max(data, function(d) {
return d.y;
})]);
data.sort(function(a, b) {
return parseDate(a.x) - parseDate(b.x);
});
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// Define the line
var lineGen = d3.svg.line()
.interpolate("monotone")
.x(function(d) {
return xScale(parseDate(d.x));
})
.y(function(d) {
return yScale(d.y);
});
svg.append('path')
.attr("class", "line")
.style("stroke", "red")
.attr('d', lineGen(data));
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: none;
stroke: steelblue;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 2;
shape-rendering: crispEdges;
}
.grid .tick {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
See another example on jsfiddle. https://jsfiddle.net/ermineia/cmqzh33x/
var data = [{
x: '1-May-12',
y: 5
}, {
x: '30-Apr-12',
y: 28
}, {
x: '27-Apr-12',
y: 58
}, {
x: '26-Apr-12',
y: 88
}, {
x: '25-Apr-12',
y: 8
}, {
x: '24-Apr-12',
y: 48
}, {
x: '23-Apr-12',
y: 28
}, {
x: '20-Apr-12',
y: 68
}, {
x: '19-Apr-12',
y: 8
}, {
x: '18-Apr-12',
y: 58
}, {
x: '17-Apr-12',
y: 5
}, {
x: '16-Apr-12',
y: 80
}, {
x: '13-Apr-12',
y: 38
}];
var margin = {
top: 30,
right: 20,
bottom: 35,
left: 50
},
width = 1200 - (margin.left + margin.right);
height = 360 - 2 * (margin.top + margin.bottom);
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y");
var xScale = d3.time.scale().range([0, width]);
var yScale = d3.scale.linear().range([height, 0]);
xScale.domain(d3.extent(data, function(d) {
return parseDate.parse(d.x);
}));
yScale.domain([0, d3.max(data, function(d) {
return d.y;
})]);
var xAxis = d3.svg.axis().scale(xScale)
.orient("bottom").ticks(0).tickSize(0)
.tickFormat("").outerTickSize(0);
var yAxis = d3.svg.axis().scale(yScale)
.orient("left").tickSize(0).ticks(0)
.tickFormat("");
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("class", "bg-color")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
function make_x_axis() {
return d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(25)
}
// function for the y grid lines
function make_y_axis() {
return d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(25);
}
svg.append("g")
.attr("class", "grid")
.call(make_x_axis()
.tickSize(height, 0, 0)
.tickFormat("")
);
// Draw the y Grid lines
svg.append("g")
.attr("class", "grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat("")
);
data.sort(function(a, b) {
return parseDate.parse(a.x) - parseDate.parse(b.x);
});
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// Define the line
var lineGen = d3.svg.line()
.interpolate("monotone")
.x(function(d) {
return xScale(parseDate.parse(d.x));
})
.y(function(d) {
return yScale(d.y);
});
svg.append('path')
.attr("class", "line")
.style("stroke", "red")
.attr('d', lineGen(data));
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: none;
stroke: steelblue;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 2;
shape-rendering: crispEdges;
}
.grid .tick {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

D3 graph with single Y value over time

I have data with a constant value across multiple dates.
[{"date":"13-Sep-15","data0":2464},{"date":"12-Sep-15","data0":2464},
{"date":"11-Sep-15","data0":2464},{"date":"10-Sep-15","data0":2464},
{"date":"9-Sep-15","data0":2464},{"date":"8-Sep-15","data0":2464},
{"date":"7-Sep-15","data0":2464}]
What are the settings of d3.axis() to get a graph like this?
This needs to work from 1->n y values.
.domain doesn't like an extent of [2464, 2464] no matter the value of .ticks.
Ok I am assuming, want to have distinct y ticks depending on the values in the data.
I am doing some thing like this to get the unique ticks needed in the y axis:
var yData = [];
data.forEach(function (d) {
d.date = parseDate(d.date);
d.close = +d.data0;
yData.push(+d.data0)
});
yData = d3.set(yData).values();//get a set of values for y axis.
Full code here:
// Set the dimensions of the canvas / graph
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the line
var valueline = d3.svg.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.close);
});
// Adds the svg canvas
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
data = [{
"date": "13-Sep-15",
"data0": 2464
}, {
"date": "12-Sep-15",
"data0": 2464
}, {
"date": "11-Sep-15",
"data0": 2464
}, {
"date": "10-Sep-15",
"data0": 2464
}, {
"date": "9-Sep-15",
"data0": 2464
}, {
"date": "8-Sep-15",
"data0": 2464
}, {
"date": "7-Sep-15",
"data0": 2464
}];
var yData = [];
data.forEach(function (d) {
d.date = parseDate(d.date);
d.close = +d.data0;
yData.push(+d.data0)
});
yData = d3.set(yData).values()
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").tickValues(yData);
// Scale the range of the data
x.domain(d3.extent(data, function (d) {
return d.date;
}));
y.domain([0, d3.max(data, function (d) {
return d.close;
})]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
body {
font: 12px Arial;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path, .axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Hope this helps!
Figured it out. Here is the graph with the line centered on single y.
// Set the dimensions of the canvas / graph
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the line
var valueline = d3.svg.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.close) / 2;
});
// Adds the svg canvas
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
data = [{
"date": "13-Sep-15",
"data0": 2464
}, {
"date": "12-Sep-15",
"data0": 2464
}, {
"date": "11-Sep-15",
"data0": 2464
}, {
"date": "10-Sep-15",
"data0": 2464
}, {
"date": "9-Sep-15",
"data0": 2464
}, {
"date": "8-Sep-15",
"data0": 2464
}, {
"date": "7-Sep-15",
"data0": 2464
}];
var yData = [];
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.data0;
yData.push(+d.data0)
});
yData = d3.set(yData).values()
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").tickValues(yData);
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain(d3.extent(data, function(d) {
return d.close;
}));
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
d3.select('.y .tick').attr("transform", "translate(0," + height / 2 + ")");
body {
font: 12px Arial;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Changes:
Translated the y tick down by height / 2:
d3.select('.y .tick').attr("transform", "translate(0," + height / 2 + ")");
Changed y extent:
y.domain(d3.extent(data, function (d) {
return d.close;
}));
Halved the y of the line:
var valueline = d3.svg.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.close) / 2;
});

d3.js - mouseover event not working properly on svg group

I have a graph for which I need a reference line everywhere the mouse-cursor is inside this graph. And this reference line will follow the mouse movements inside the graph.
But this doesn't seems to work fine. It works only on the axis and the ticks (.axis lines) of the axis. On debugging, I found that mouse event works fine when applied over SVG but not on the group, why so ?
Here is my code :
test.html
<html>
<head>
<script src="jquery.js">
</script>
<script src="d3.v2.js">
</script>
<script src="retest.js">
</script>
<style type="text/css">
.g_main {
cursor:pointer;
}
.axis path, .axis line {
stroke: #DBDBDB;
/*shape-rendering: crispEdges;
*/
}
.y g:first-child text {
display:none;
}
.y g:first-child line {
stroke: #989898 ;
stroke-width: 2.5px;
}
/*.x g:first-child line {
stroke: black ;
stroke-width: 2.5px;
}
*/
.y path {
stroke: #989898 ;
stroke-width: 2.5px;
}
</style>
</head>
<body>
<center>
<button id="reload" onclick="loadViz();">
load Graph
</button>
<div id="viz" class="viz">
</div>
</center>
<script>
loadViz();
</script>
</body>
</html>
retest.js
var series,
classifications,
minVal,
maxVal,
svgW = 600,
svgH = 600,
//w = 1200,
//h = 1200,
vizPadding = {
top: 120,
right: 30,
bottom: 120,
left: 50
},
yAxMin_PA = 0,
yAxMax_PA = 50,
xAxMin_PA = 2002,
xAxMax_PA = 2008,
areaStrokeColors = ['#FF6600', '#3366FF', '#B8860B', '#458B00', 'white'];
var loadViz = function () {
color = d3.scale.category10();
data = {
"lines": [{
"line": [{
"X": 2002,
"Y": 42
}, {
"X": 2003,
"Y": 45
},
{
"X": 2005,
"Y": 47
},
{
"X": 2007,
"Y": 41
}
]
}, {
"line": [{
"X": 2003,
"Y": 33
}, {
"X": 2005,
"Y": 38
}, {
"Y": 36,
"X": 2008
}
]
}, {
"line": [{
"X": 2004,
"Y": 13
}, {
"X": 2005,
"Y": 19
}, {
"X": 2008,
"Y": 21
}
]
}, {
"line": [{
"X": 2003,
"Y": 20
}, {
"X": 2005,
"Y": 27
}, {
"X": 2008,
"Y": 29
}
]
}
]
};
$("#viz").html("");
buildBase();
//setScales();
};
var buildBase = function () {
margin = {
top: 80,
right: 120,
bottom: 40,
left: 40
},
width = 960 - margin.left - margin.right,
height = 550 - margin.top - margin.bottom;
t2 = height + margin.top + margin.bottom;
x = d3.scale.linear()
.domain([xAxMin_PA, xAxMax_PA])
.range([0, width]);
y = d3.scale.linear()
.domain([yAxMin_PA, yAxMax_PA])
.range([height, 0]);
tickSizeToApplyX = 5;
tickSizeToApplyY = 10;
// Function to draw X-axis
xAxis = d3.svg.axis()
.scale(x)
.ticks(tickSizeToApplyX)
.tickSize(-height, 0, 0)
//.tickSize(10)
.orient("bottom")
.tickPadding(5);
// Function to draw Y-axis
yAxis = d3.svg.axis()
.scale(y)
.ticks(tickSizeToApplyY)
.tickSize(-width, 0, 0)
//.tickSize(0)
.orient("left")
.tickPadding(5);
// Define the line
var valueline = d3.svg.line()
.x(function (d) { /*console.log(d.X);*/
return x(d.X);
})
.y(function (d) { /*console.log(d.Y);*/
return y(d.Y);
});
// Define the line
var referline = d3.svg.line()
.x(function (dx) { /*console.log(d.X);*/
return dx;
})
.y(function (dy) { /*console.log(d.Y);*/
return dy;
});
// Append SVG into the html
var viz = d3.select("#viz")
.append("svg")
.attr("width", width + margin.left + margin.right + 10)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "g_main")
.attr("transform", "translate(" + margin.left + "," + ((margin.top) - 30) + ")");
viz.on("mousemove", function () {
cx = d3.mouse(this)[0];
cy = d3.mouse(this)[1];
console.log("xx=>" + cx + "yy=>" + cy);
redrawline(cx, cy);
})
.on("mouseover", function () {
d3.selectAll('.line_over').style("display", "block");
})
.on("mouseout", function () {
d3.selectAll('.line_over').style("display", "none");
});
//console.log(this);
viz.append("line")
//d3.select("svg").append("line")
.attr("class", 'line_over')
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", x(xAxMax_PA))
.attr("y2", 0)
.style("stroke", "gray")
.attr("stroke-dasharray", ("5,5"))
.style("stroke-width", "1.5")
.style("display", "none");
// Draw X-axis
viz.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Draw Y-axis
viz.append("g")
.attr("class", function (d, i) {
return "y axis"
})
.call(yAxis);
function redrawline(cx, cy) {
d3.selectAll('.line_over')
.attr("x1", 0)
.attr("y1", cy)
.attr("x2", x(xAxMax_PA))
.attr("y2", cy)
.style("display", "block");
}
};
The g element is just an empty container which cannot capture click events (see documentation for pointer-events property for details).
However, mouse events do bubble up to it. Hence, the effect you desire can be achieved by first making sure that the g receives all pointer events:
.g_main {
// ..
pointer-events: all;
}
And then appending an invisible rectangle to it as a place to hover over:
viz.on("mousemove", function () {
cx = d3.mouse(this)[0];
cy = d3.mouse(this)[1];
redrawline(cx, cy);
})
.on("mouseover", function () {
d3.selectAll('.line_over').style("display", "block");
})
.on("mouseout", function () {
d3.selectAll('.line_over').style("display", "none");
})
.append('rect')
.attr('class', 'click-capture')
.style('visibility', 'hidden')
.attr('x', 0)
.attr('y', 0)
.attr('width', width)
.attr('height', height);
Working example: http://jsfiddle.net/H3W3k/
As for why they work when applied to the svg element (from the docs):
Note that the ‘svg’ element is not a graphics element, and in a Conforming SVG Stand-Alone File a rootmost ‘svg’ element will never be the target of pointer events, though events can bubble to this element. If a pointer event does not result in a positive hit-test on a graphics element, then it should evoke any user-agent-specific window behavior, such as a presenting a context menu or controls to allow zooming and panning of an SVG document fragment.

How to create a horizontal legend with d3.js

I've been trying to create a horizontal legend for my graph using d3.js. I've struggled to get the x-axis spacing correct with dynamic labels.
The problem is that the labels are not of a consistent width, here's a full example and this is my function to calculate the x position:
function legendXPosition(data, position, avgFontWidth){
if(position == 0){
return 0;
} else {
var xPostiion = 0;
for(i = 0; i < position; i++){
xPostiion += (data[i].length * avgFontWidth);
}
return xPostiion;
}
}
Does anyone have any suggestions on how to improve this?
I suggest referencing this question: SVG get text element width
Render the first legend entry as you already are. Store this entry, or assign ids such that you can look them up through selection.
When rendering subsequent entries, get the previous 'text' element and x offset. Compute the new legend entry offset using the exact width of the previous text element
var myNewXOffset = myPreviousXOffset + myPreviousText.getBBox().width
Modified the code to get the following result
The reason your labels are not of a consistent width is due to two factors. Both of them are related to your function to calculate the x position:
the avgFontWidth parameter
It should be smaller. You assigned it the value of 15 in your example, but the actual averageFontWidth is way smaller than 15, it was between 5 to 7. The extra space between your legend labels come from this inaccurate value of average font width.
Your avgFontWidth is just an average. In reality, fonts width change.For example, the r in the picture below is way narrower than the P.
Thus the five letter word rrrrr can be way shorter than PPPPP when the font family is set in some way. In conclusion, you shall not use avgFontWidth. You should use SVG get text element width as cmonkey suggests
the logic of xPostiion += (data[i].length * avgFontWidth)
The above formula intends to calculate the length of the text, but the width of the label shall also be added, xPostiion should be
xPostiion += (data[i].length * avgFontWidth) + widthOftheLabel
The modified code:
function drawlinegraph(data, startDateString, endDateString, maxValue, minValue, colour,
divID, labels) {
var m = {
top: 60,
right: 0,
bottom: 35,
left: 80
},
w = 770 - m.left - m.right,
h = 180 - m.top - m.bottom,
dateFormat = "%Y-%m-%d";
labels = ["ada", "adas", "asdasdasd", "sd"];
var parseDate = d3.time.format(dateFormat).parse;
var startDate = parseDate(startDateString);
var endDate = parseDate(endDateString);
// Define the x scale
var x = d3.time.scale()
.domain([startDate, endDate])
.range([0, w]);
// Format x-axis labels
x.tickFormat(d3.time.format(dateFormat));
// Define the y scale
var y = d3.scale.linear()
.domain([minValue, maxValue])
.range([h, 0]);
var graph = d3.select(divID).append("svg:svg")
.attr("width", w + m.right + m.left)
.attr("height", h + m.top + m.bottom)
.append("svg:g")
.attr("transform", "translate(" + m.left + "," + m.top + ")");
// create x-axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(4)
.tickSize(-h)
.tickFormat(d3.time.format("%Y/%m"));
// Add the x-axis.
graph.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
// Overide the default behaviour of d3 axis labels
d3.selectAll(".x.axis g text")[0].forEach(function(e) {
e.attributes[0].value = 8; // y
});
// create y-axis
var yAxisLeft = d3.svg.axis()
.scale(y)
.ticks(12)
.orient("left")
.tickSize(-w);
// Add the y-axis to the left
graph.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxisLeft);
var i = 0;
$.each(data, function(key, value) {
value.forEach(function(d) {
d.date = parseDate(d.date);
});
var line = d3.svg.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.value);
});
graph.append("path")
.datum(value)
.attr("d", line)
.attr("class", "line")
.attr("stroke", colour[i]);
i++;
});
var legend = graph.append("g")
.attr("class", "legend")
.attr("height", 100)
.attr("width", 100)
.attr('transform', 'translate(-5,' + (h + 35) + ')');
legend.selectAll('rect')
.data(labels)
.enter()
.append("rect")
.attr("x", function(d, i) {
var xPost = legendXPosition(labels, i, 6);
return xPost;
})
.attr("y", -6)
.attr("width", 20)
.attr("height", 5)
.style("fill", function(d, i) {
var color = colour[i];
return color;
});
legend.selectAll('text')
.data(labels)
.enter()
.append("text")
.attr("x", function(d, i) {
var xPost = legendXPositionText(labels, i, 22, 6);
return xPost;
})
.attr("y", -1)
.text(function(d) {
return d;
});
};
function legendXPositionText(data, position, textOffset, avgFontWidth) {
return legendXPosition(data, position, avgFontWidth) + textOffset;
}
function legendXPosition(data, position, avgFontWidth) {
if (position == 0) {
return 0;
} else {
var xPostiion = 0;
for (i = 0; i < position; i++) {
xPostiion += (data[i].length * avgFontWidth + 40);
}
return xPostiion;
}
}
var benchmark_line_graph_colours = ["#524364", "#937ab1", "#ab5b02", "#faa757"],
benchmark_line_graph_data = {
"Beassa ALBI TR ZAR": [{
"date": "2012-08-31",
"value": 101.1
}, {
"date": "2012-09-28",
"value": 101.89
}, {
"date": "2012-10-31",
"value": 101.09
}],
"FTSE/JSE All Share TR ZAR": [{
"date": "2012-08-31",
"value": 99.72
}, {
"date": "2012-09-28",
"value": 101.24
}, {
"date": "2012-10-31",
"value": 105.29
}],
"STeFI Composite ZAR": [{
"date": "2012-08-31",
"value": 100.23
}, {
"date": "2012-09-28",
"value": 100.52
}, {
"date": "2012-10-31",
"value": 100.77
}],
"portfolio": [{
"date": "2012-08-31",
"value": 101.55
}, {
"date": "2012-09-28",
"value": 101.15
}, {
"date": "2012-10-31",
"value": 102.08
}]
};
drawlinegraph(benchmark_line_graph_data,
"2012-08-31",
"2012-10-31",
105.84700000000001,
99.163, benchmark_line_graph_colours,
"body");
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis text {
padding-top: 5px;
text-anchor: "right";
}
.line {
fill: none;
stroke-width: 1.5px;
}
.y.axis line,
.y.axis path {
stroke-dasharray: 2, 2;
}
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.js"></script>

Categories

Resources