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>
Related
I know it's little weird, I do have a requirement to have bar chart with fixed width instead of dynamic width handling. This is implemented with below code by handling x and width, now the spacing between the bars more when there is only few bars. So is there any possibility in d3 to render x axis values and chart in center of the axis line with fixed bar width and fixed spacing between bars?
bars.append('rect')
.attr('class', function(d,i) {
return 'bar';
})
.attr('id', function(d, i) {
return name+'-bar-'+i;
})
.attr('x', function(d) {
return xScale(d.month) + (xScale.rangeBand() - 15)/2;
})
.attr('width', function(){
return Math.min(xScale.rangeBand()-2, 15);
})
.attr('y', function(d) { return yScale(d.set.count); })
.attr('height', function(d) { return height - yScale(d.set.count); })
.on('click', function(d, i) {
//
});
fiddle
An easy way to do that is setting your xScale range dynamically:
var xScaleRange = data.length * 30;
var xScale = d3.scale.ordinal()
.rangeRoundBands([width / 2 - xScaleRange, width / 2 + xScaleRange], 0.2);
The magic number 30 here is simply twice the bar width.
Here is your code with that change:
// container size
var margin = {
top: 10,
right: 10,
bottom: 30,
left: 30
},
width = 600,
height = 300;
var data = [{
"month": "DEC",
"set": {
"count": 26,
"id": 1,
"label": "Set",
"year": "2016"
}
}, {
"month": "JAN",
"set": {
"count": 30,
"id": 1,
"label": "Set",
"year": "2017"
}
}, {
"month": "FEB",
"set": {
"count": 30,
"id": 1,
"label": "Set",
"year": "2017"
}
}];
var name = 'dashboard';
var xScaleRange = data.length * 30;
// x scale
var xScale = d3.scale.ordinal()
.rangeRoundBands([width / 2 - xScaleRange, width / 2 + xScaleRange], 0.2);
// set x and y scales
xScale.domain(data.map(function(d) {
return d.month;
}));
// x axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.outerTickSize(0);
var yScale = d3.scale.linear()
.domain([0, d3.max(data, function(d) {
return d.set.count;
})])
.range([height, 0]);
var ticks = yScale.ticks(),
lastTick = ticks[ticks.length - 1];
var newLastTick = lastTick + (ticks[1] - ticks[0]);
if (lastTick < yScale.domain()[1]) {
ticks.push(lastTick + (ticks[1] - ticks[0]));
}
// adjust domain for further value
yScale.domain([yScale.domain()[0], newLastTick]);
// y axis
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(-width, 0, 0)
.tickFormat(d3.format('d'))
.tickValues(ticks);
// create svg container
var svg = d3.select('#chart')
.append('svg')
.attr('class', 'd3-setup-barchart')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Horizontal grid (y axis gridline)
svg.append('g')
.attr('class', 'grid horizontal')
.attr("transform", "translate(" + (width / 2 - xScaleRange) + ",0)")
.call(d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(-xScaleRange * 2, 0, 0)
.tickFormat('')
.tickValues(ticks)
);
// create bars
var bars = svg.selectAll('.bar')
.data(data)
.enter()
.append('g');
bars.append('rect')
.attr('class', function(d, i) {
return 'bar';
})
.attr('id', function(d, i) {
return name + '-bar-' + i;
})
.attr('x', function(d) {
return xScale(d.month) + (xScale.rangeBand() - 15) / 2;
})
.attr('width', function() {
return Math.min(xScale.rangeBand() - 2, 15);
})
.attr('y', function(d) {
return yScale(d.set.count);
})
.attr('height', function(d) {
return height - yScale(d.set.count);
})
.on('click', function(d, i) {
//
});
// draw x axis
svg.append('g')
.attr('id', name + '-x-axis')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
// draw y axis
svg.append('g')
.attr('class', 'y axis')
.attr("transform", "translate(" + (width / 2 - xScaleRange) + ",0)")
.call(yAxis)
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 6)
.attr('dy', '.71em')
.style('text-anchor', 'end');
// remove 0 in y axis
svg.select('.y')
.selectAll('.tick')
.filter(function(d) {
return d === 0 || d % 1 !== 0;
}).remove();
svg
.select('.horizontal')
.selectAll('.tick')
.filter(function(d) {
return d === 0 || d % 1 !== 0;
}).remove();
.d3-setup-barchart {
background-color: #666666;
}
.d3-setup-barchart .axis path {
fill: none;
stroke: #000;
}
.d3-setup-barchart .bar {
fill: #ccc;
}
.d3-setup-barchart .bar:hover {
fill: orange;
cursor: pointer;
}
.d3-setup-barchart .bar.selected {
fill: orange;
stroke: #fff;
stroke-width: 2;
}
.d3-setup-barchart .label-text {
text-anchor: middle;
font-size: 12px;
font-weight: bold;
fill: orange;
opacity: 0;
}
.d3-setup-barchart .label-text.selected {
opacity: 1;
}
.d3-setup-barchart .axis text {
fill: rgba(255, 255, 255, 0.6);
font-size: 9px;
}
.d3-setup-barchart .axis-text.selected {
fill: orange;
}
.d3-setup-barchart .y.axis path {
display: none;
}
.d3-setup-barchart .y.axis text {
font-size: 6px;
}
.d3-setup-barchart .x.axis path {
fill: none;
stroke: #353C41;
}
.d3-setup-barchart .grid .tick {
stroke: #fff;
opacity: .18 !important;
stroke-width: 0;
}
.d3-setup-barchart .grid .tick line {
stroke-width: .5 !important;
}
.d3-setup-barchart .grid path {
stroke-width: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chart"></div>
I am kind a new to d3.js and want to draw a horizontal grid line based on y coordinate value fiddle , i've tried with if condition at y1 and y2 locations but the lines are overlapping at the top of graph.
Any help is much appreciated.
Your if statement makes little sense to me:
if (y(d) === thresholdValues.minValue) {
return y(d);
}
This means that only when the value in the screen is exactly thresholdValues.minValue (which is 40 in your code) the line will be painted.
Solution: drop the if statement. Actually, I kept the if just to avoid the first gridline, over the x axis:
if (d != 0) {
return y(d);
}
Here is your code with that change:
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
};
var thresholdValues = {
minValue: 40,
maxValue: 85
};
var width = 600 - margin.left - margin.right;
var height = 270 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var valueline = d3.svg.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.close);
});
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 data = [{
date: "1-May-12",
close: "58.13"
}, {
date: "30-Apr-12",
close: "53.98"
}, {
date: "27-Apr-12",
close: "67.00"
}, {
date: "26-Apr-12",
close: "89.70"
}, {
date: "25-Apr-12",
close: "99.00"
}];
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, 100]);
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);
svg.selectAll("line.horizontalGrid").data(y.ticks(4)).enter()
.append("line")
.attr({
"class": "horizontalGrid",
"x1": margin.right,
"x2": width - margin.right,
"y1": function(d) {
if (d != 0) {
return y(d);
}
},
"y2": function(d) {
if (d != 0) {
return y(d);
}
},
"fill": "none",
"shape-rendering": "crispEdges",
"stroke": "grey",
"stroke-width": "2px",
"opacity": 0.4,
"stroke-dasharray": 8
});
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>
EDIT: If you want to draw only two lines, at the thresholds, you have to pass their values to data, not the scale ticks:
.data(Object.values(thresholdValues))
Here is the demo:
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
};
var thresholdValues = {
minValue: 40,
maxValue: 85
};
var width = 600 - margin.left - margin.right;
var height = 270 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var valueline = d3.svg.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.close);
});
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 data = [{
date: "1-May-12",
close: "58.13"
}, {
date: "30-Apr-12",
close: "53.98"
}, {
date: "27-Apr-12",
close: "67.00"
}, {
date: "26-Apr-12",
close: "89.70"
}, {
date: "25-Apr-12",
close: "99.00"
}];
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, 100]);
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);
svg.selectAll("line.horizontalGrid").data(Object.values(thresholdValues)).enter()
.append("line")
.attr({
"class": "horizontalGrid",
"x1": margin.right,
"x2": width - margin.right,
"y1": function(d) {
if (d != 0) {
return y(d);
}
},
"y2": function(d) {
if (d != 0) {
return y(d);
}
},
"fill": "none",
"shape-rendering": "crispEdges",
"stroke": "grey",
"stroke-width": "2px",
"opacity": 0.4,
"stroke-dasharray": 8
});
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://d3js.org/d3.v3.js"></script>
I am trying to draw a Dovish & Hawkish line chart diagram.
Example:
However, it's throwing an error.
I took the example from a bar chart, that draws the bars with a positive and negative value. Then I have used the code only related to draw the xScale/yScale and the lines for xAxis/yAxis.
JSFiddle can be found here.
The problem here is that you are using an ordinal scale for the x axis:
var x = d3.scale.ordinal()
.domain(d3.range(data.length))
Which has this domain:
[0, 1, 2, 3, 4, 5, 6, 7]
However, you're passing the datum to the line generator:
var line = d3.svg.line()
.x(function(d) { return x(d);})
//datum here -------------^
Instead of that, pass the index of the datum:
var line = d3.svg.line()
.x(function(d, i) { return x(i);})
//index here ----------------^
Here is your updated code:
var data = [-0.1, -0.5, -0.32, 0.2, 1, 0.5, -0.3, -1.0];
var margin = {
top: 30,
right: 10,
bottom: 10,
left: 30
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var y0 = Math.max(Math.abs(d3.min(data)), Math.abs(d3.max(data)));
var y = d3.scale.linear()
.domain([-y0, y0])
.range([height, 0])
.nice();
var x = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeRoundBands([0, width], .2);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var line = d3.svg.line()
.x(function(d, i) {
return x(i);
})
.y(function(d) {
return y(d);
});
var dataSum = d3.sum(data, function(d) {
return d;
});
svg.append("g")
.attr("class", "x axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.append("line")
.attr("y1", y(0))
.attr("y2", y(0))
.attr("x1", 0)
.attr("x2", width);
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", line);
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line{
fill: none;
stroke: blue;
stroke-width: 2px;
}
.tick text{
font-size: 12px;
}
.tick line{
opacity: 0.2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
PS: You don't need that awkward line in the middle of the chart. You can create a proper axis:
var data = [-0.1, -0.5, -0.32, 0.2, 1, 0.5, -0.3, -1.0];
var margin = {
top: 30,
right: 10,
bottom: 10,
left: 30
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var y0 = Math.max(Math.abs(d3.min(data)), Math.abs(d3.max(data)));
var y = d3.scale.linear()
.domain([-y0, y0])
.range([height, 0])
.nice();
var x = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeRoundBands([0, width], .2);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var line = d3.svg.line()
.x(function(d, i) {
return x(i);
})
.y(function(d) {
return y(d);
});
var dataSum = d3.sum(data, function(d) {
return d;
});
svg.append("g")
.attr("class", "x axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0," + y(0) + ")")
.call(xAxis)
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", line);
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: blue;
stroke-width: 2px;
}
.tick text {
font-size: 12px;
}
.tick line {
opacity: 0.2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
so I am trying to make something where the user can drag and create two equal sized brushes in a line chart, the problem is that brush disappears after I try to draw the second one. I have tried to create separate attr and seperate brush call but still can't do it.
this is my code https://jsfiddle.net/f0gxs41t/
any help?
<!DOCTYPE html>
<html>
<head>
<svg width="960" height="400"></svg>
<meta charset="utf-8">
<title>
line chart with drag and drop
</title>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script src="./line_graph.js"></script>
<link rel="stylesheet" type= "text/css" href="./style.css">
</head>
<body>
</body>
</html>
js file :
var data=[1,5,2,7,4,7,8,9,5,3,6,8,2,3,5,9,8,5]
var svg=d3.select("svg")
var margin={top:100,bottom:50,left:100,right:0},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x_extent=d3.extent(data,function(d,i){return i})
var y_extent=d3.extent(data,function(d,i){return d})
x=d3.scale.linear()
.range([0,width])
.domain(x_extent)
y=d3.scale.linear()
.range([height,0])
.domain(y_extent)
var line=d3.svg.line()
.x(function(d,i){ return x(i)})
.y(function(d,i){return y(d)})
var xAxis = d3.svg.axis().scale(x)
.orient("bottom");
var yAxis = d3.svg.axis().scale(y)
.orient("left");
// g.append("g")
// .append("text")
// .attr("fill", "#000")
// .attr("transform","rotate(-90)")
// .attr("y",-35)
// .attr("dy","0.71em")
// .attr("text-anchor","end")
// .text("break something")
g.append("path")
.attr("class","line")
.datum(data)
.attr("d",line)
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+margin.left+"," + (height+margin.top) + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate("+margin.left+ ","+margin.top+")")
.call(yAxis);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// if (brush.empty()!==true){
//
// }
var brushes = [];
var parent_brush=d3.svg.brush()
.x(x)
.on("brushend",brushed)
var brush1=d3.svg.brush()
.x(x)
.on("brushend",brushed)
function brushed(){
if (parent_brush.empty()==true){
console.log(parent_brush,"empty")
}
else {
brush_width=parent_brush.extent()[1]-parent_brush.extent()[0]
console.log(parent_brush,"not empty")
}
}
svg.append("g")
.attr("class","parent")
.call(parent_brush)
.selectAll("rect")
.attr("x",margin.left)
.attr("y",margin.top)
.attr("height",height)
.style("fill","orange")
.style("fill-opacity",".2")
svg.append("g")
.attr("class","child")
.call(brush1)
.selectAll("rect")
.attr("x",margin.left)
.attr("y",margin.top)
.attr("height",height)
.style("fill","orange")
.style("fill-opacity",".2")
css file:
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.hover-line {
stroke: #6F257F;
stroke-width: 2px;
stroke-dasharray: 3,3;
}
.area {
fill: lightsteelblue;
}
.line{
fill:none;
stroke:brown;
stroke-linejoin:round;
stroke-linecap:round;
stroke-width:1.5
}
.focus circle {
fill: none;
stroke: steelblue;
}
.brush {
fill: grey;
pointer-events: all;
fill-opacity:0.3;
}
.resize {
fill: grey;
pointer-events: all;
fill-opacity:0.7;
}
consider using d3.js v4...
Child brush is created in the parent brush "end" event handler, where we also need to disable new parent brush selections.
var parent_brush = d3.brushX()
.extent([
[margin.left, margin.top],
[margin.left + width, margin.top + height]
])
.on("end", brushedParent);
var child_brush;
svg.append("g")
.attr("class", "parent")
.call(parent_brush);
function brushedParent() {
// remove new brush selection capture area
svg.select('.parent .overlay').remove();
if (!child_brush) {
child_brush = d3.brushX()
.extent([
[margin.left, margin.top],
[margin.left + width, margin.top + height]
])
.on("end", brushedChild);
svg.append("g")
.attr("class", "child")
.call(child_brush);
}
}
function brushedChild() {
// remove new brush selection capture area
svg.select('.child .overlay').remove();
child_selection = d3.brushSelection(svg.select('.child').node());
var parent_selection = d3.brushSelection(svg.select('.parent').node());
var parent_width = parent_selection[1] - parent_selection[0];
var resized_child = [child_selection[0], child_selection[0] + parent_width];
child_brush.on("end", null);
child_brush.move(svg.select('.child'), resized_child);
}
Here is the working code snippet:
var data = [1, 5, 2, 7, 4, 7, 8, 9, 5, 3, 6, 8, 2, 3, 5, 9, 8, 5]
var svg = d3.select("svg")
var margin = {
top: 100,
bottom: 50,
left: 100,
right: 0
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x_extent = d3.extent(data, function(d, i) {
return i
})
var y_extent = d3.extent(data, function(d, i) {
return d
})
x = d3.scaleLinear()
.range([0, width])
.domain(x_extent)
y = d3.scaleLinear()
.range([height, 0])
.domain(y_extent)
var line = d3.line()
.x(function(d, i) {
return x(i)
})
.y(function(d, i) {
return y(d)
})
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
g.append("path")
.attr("class", "line")
.datum(data)
.attr("d", line)
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + (height + margin.top) + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(yAxis);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var brushes = [];
var parent_brush = d3.brushX()
.extent([
[margin.left, margin.top],
[margin.left + width, margin.top + height]
])
.on("end", brushedParent);
var child_brush;
svg.append("g")
.attr("class", "parent")
.call(parent_brush);
function brushedParent() {
// remove new brush selection capture area
svg.select('.parent .overlay').remove();
if (!child_brush) {
child_brush = d3.brushX()
.extent([
[margin.left, margin.top],
[margin.left + width, margin.top + height]
])
.on("end", brushedChild);
svg.append("g")
.attr("class", "child")
.call(child_brush);
}
}
function brushedChild() {
// remove new brush selection capture area
svg.select('.child .overlay').remove();
child_selection = d3.brushSelection(svg.select('.child').node());
var parent_selection = d3.brushSelection(svg.select('.parent').node());
var parent_width = parent_selection[1] - parent_selection[0];
var resized_child = [child_selection[0], child_selection[0] + parent_width];
child_brush.on("end", null);
child_brush.move(svg.select('.child'), resized_child);
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.hover-line {
stroke: #6F257F;
stroke-width: 2px;
stroke-dasharray: 3, 3;
}
.area {
fill: lightsteelblue;
}
.line {
fill: none;
stroke: brown;
stroke-linejoin: round;
stroke-linecap: round;
stroke-width: 1.5
}
.focus circle {
fill: none;
stroke: steelblue;
}
.brush {
fill: grey;
pointer-events: all;
fill-opacity: 0.3;
}
.resize {
fill: grey;
pointer-events: all;
fill-opacity: 0.7;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="400"></svg>
I want to draw an xy multiseries linechart with d3.js. Thats ok.
But afterwards I want to scale the x-axes ordinal.
so thats the code of my xy linechart:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 12px Arial;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
div.tooltip {
position: absolute;
text-align: center;
padding: 5px;
font: 14px sans-serif;
background: black;
color: white;
border: 0px;
border-radius: 8px;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1px;
}
</style>
<body>
<script src="d3.js"></script>
<script src="jquery-2.1.4.js" charset="utf-8"></script>
<script>
var margin = {
top: 20,
right: 20,
bottom: 20,
left: 50
},
width = 1180 - margin.left - margin.right,
height = 580 - margin.top - margin.bottom;
var x = d3.scale.linear().rangeRound([0, width]);
var y = d3.scale.linear().rangeRound([height, 0]);
var linearScale = d3.scale.linear();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
/* var line = d3.svg.line()
.x(function(d) { console.log('x'+x(d.arbeitsgang));return x(d.arbeitsgang); })
.y(function(d) { console.log('y'+y(d.koordinaten));return y(d.koordinaten); });
*/
var line = d3.svg.line()
.x(function (d) {
return x(d.x);
})
.y(function (d) {
return y(d.y);
});
// Define 'div' for tooltips
var div = d3.select("body")
.append("div") // declare the tooltip div
.attr("class", "tooltip") // apply the 'tooltip' class
.style("opacity", 0); // set the opacity to nil
//d3.json("Arbeitsgang.json", function(error, data) {
var data = [
{
"key": "Paket 1",
"values": [
{
"x": 0,
"y": 40,
"arbeitsgang": "A1"
},
{
"x": 6,
"y": 30,
"arbeitsgang": "A2"
},
{
"x": 17,
"y": 20,
"arbeitsgang": "A3"
}
]
},
{
"key": "Paket 3",
"values": [
{
"x": 0,
"y": 85,
"arbeitsgang": "A1"
},
{
"x": 8,
"y": 50,
"arbeitsgang": "A2"
},
{
"x": 17,
"y": 89,
"arbeitsgang": "A3"
}
]
},
{
"key": "Paket 2",
"values": [
{
"x": 0,
"y": 45,
"arbeitsgang": "A1"
},
{
"x": 6,
"y": 145,
"arbeitsgang": "A1"
},
{
"x": 17,
"y": 53,
"arbeitsgang": "A1"
}
]
}
];
linearScale.domain(d3.keys(data[0]).filter(function (key) {
return key;
}));
x.domain([
d3.min(data, function (c) {
return d3.min(c.values, function (v) {
return v.x;
});
}),
d3.max(data, function (c) {
return d3.max(c.values, function (v) {
return v.x;
});
})
]);
y.domain([
d3.min(data, function (c) {
return d3.min(c.values, function (v) {
return v.y;
});
}),
d3.max(data, function (c) {
return d3.max(c.values, function (v) {
return v.y;
});
})
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var graphen = svg.selectAll(".graphen")
.data(data)
.enter().append("g")
.attr("class", "graphen");
var graph = graphen.append("path")
.attr("class", "line")
.attr("d", function (d) {
return line(d.values);
});
graph.on("mouseover", function (d) {
d3.select(this).style("stroke-width", 7);
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d.key)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
div.style("visibility", "visible");
var selectthegraphs = $('.line').not(this); //select all the rest of the lines, except the one you are hovering on and drop their opacity
d3.selectAll(selectthegraphs)
.style("opacity", 0.2);
var selectcircles = $('.circle');
d3.selectAll(selectcircles)
.style("opacity", 0.2);
})
.on("mouseout", function () {
d3.select(this).style("stroke-width", 1);
div.transition()
.duration(500)
.style("opacity", 0.01);
div.style("visibility", "hidden");
var selectthegraphs = $('.line'); //select all the rest of the lines, except the one you are hovering on and drop their opacity
d3.selectAll(selectthegraphs)
.style("opacity",1);
var selectcircles = $('.circle');
d3.selectAll(selectcircles)
.style("opacity", 1);
});
graphen.each(function (p, j) {
d3.select(this).selectAll("circle")
.data(p.values)
.enter().append("circle")
.style("stroke", "black")
.style("fill", "white")
.attr("class","circle")
.attr("r", 5)
.attr("cx", function (d) {
return x(d.x);
})
.attr("cy", function (d) {
return y(d.y);
})
.on("mouseover", function (d) {
d3.select(this).transition().duration(500)
.attr("r", 10);
div.transition()
.duration(500)
.style("opacity", 0.9);
div.style("visibility", "visible");
div.html("X: " + d.x + "<br/>" + "Y: " + d.y + "<br/>" + "Arbeitsgang: " + d.arbeitsgang)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function (d) {
d3.select(this).transition().duration(500)
.attr("r", 5);
div.transition()
.duration(500)
.style("opacity", 0);
div.style("visibility", "hidden");
})
});
ok. but now I want the same graph with an ordinal scale.
The ticks shall be "data.values.arbeitsgang"
"arbeitsgang": "A1" for example.
please help me
Ordinal scales are not so tough.
var x = d3.scale.ordinal()
.domain(["A1","A2","A3"])
.rangeRoundPoints([0, width]);
The scale should now divide the range going from 0 to your width into 3 equally large parts. I choose for rangeRoundPoints because the values of the ticks are rounded to integers then and I like integers.
I believe you can customize the text of the x axis (of course using your ordinal scale to scale the axis), showing "Arbeitsgang:A1" for example, but I wouldn't know how to do that right on the top of my head.
For more information about ordinal scales, check out this link. Unless I totally misunderstood the question, I think this is the only thing you need.
I realize that I have hardcoded the values of the domain, but an array containing the values will do as well.