I downloaded the javascript code from this page https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172. I would like to make a change, in fact the html page takes data from a csv file, while I wanted to take data directly from an array on the same html page.
That is, I would like to create a simple array of numbers and an array of months within index.html and from there to take data.
This is the piece where the data are taken:
d3.csv("sp500.csv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.price; })]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
});
While I would like to have an array of numbers and an array di months on the html page, without using the csv:
var price = [9, 6, 2];
var date = [Jan 2000, Feb 2000, Mar 2000]
How can I do? Thanks
Here's a bl.ock that takes an array as an input.
var data = [{date: 'Jan 2000', price: 9}, {date: 'Feb 2000', price:6},{date: 'Mar 2000', price: 2}];
data.forEach(function (d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
A snippet for the same:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.area {
fill: steelblue;
clip-path: url(#clip);
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 110, left: 40},
margin2 = {top: 430, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
height2 = +svg.attr("height") - margin2.top - margin2.bottom;
var parseDate = d3.timeParse("%b %Y");
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var area = d3.area()
.curve(d3.curveMonotoneX)
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.price); });
var area2 = d3.area()
.curve(d3.curveMonotoneX)
.x(function(d) { return x2(d.date); })
.y0(height2)
.y1(function(d) { return y2(d.price); });
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var data = [{date: 'Jan 2000', price: 9}, {date: 'Feb 2000', price:6},{date: 'Mar 2000', price: 2}];
data.forEach(function (d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.price; })]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.select(".area").attr("d", area);
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.select(".area").attr("d", area);
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
</script>
Hope this helps.
you can just skip the csv function and use your own data
instead of
d3.csv("sp500.csv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
//...
})
you can do:
var data = [{date:'Jan 2000', price: 9}, {}, {}, ...];
x.domain(d3.extent(data, function(d) { return d.date; }));
//...
if you want to keep it a function you can also:
var process = function(data) {
x.domain(d3.extent(data, function(d) { return d.date; }));
//...
})
process({date:'Jan 2000', price: 9}, {}, {}, ...]); // <-- the data
Related
Currently I am learning d3js, one of the feature i like to implement is showing tooltip and zooming horizontally. I figured out how to add zooming in the chart (working fiddle) but feeling little complex in adding tooltip when hover over the points. Is it possible in d3js. Because when zooming we are adding rect element on the svg element. if we add the rect element in the chart means how to make this tooltip works. Need some help from d3 ninjas.
var data = [{
date: "10:30:00",
price: 36000
},
{
date: "11:00:20",
price: 40000
},
{
date: "12:00:00",
price: 38000
},
{
date: "14:20:00",
price: 50400
}
];
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 110,
left: 40
},
margin2 = {
top: 430,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
height2 = +svg.attr("height") - margin2.top - margin2.bottom;
var parseDate = d3.timeParse("%H:%M:%S"); //"%b %Y");
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var area = d3.line()
//.curve(d3.curveMonotoneX)
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.price);
});
var area2 = d3.line()
.curve(d3.curveMonotoneX)
.x(function(d) {
return x2(d.date);
})
.y(function(d) {
return y2(d.price);
});
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
function update() {
for (var k in data) {
type(data[k]);
}
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.price;
})]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
focus.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "circle")
.attr("r", 5)
.style("fill", 'orange')
.style("stroke", 'red')
.style("stroke-width", "2")
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.price);
});
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "circle")
.attr("r", 1)
.style("fill", 'blue')
.style("stroke", 'red')
.style("stroke-width", "2")
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.price);
});
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
}
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.select(".area").attr("d", area);
focus.selectAll('.circle')
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.price);
});
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.select(".area").attr("d", area);
focus.selectAll('.circle')
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.price);
});
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
context.selectAll('.circle')
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.price);
});
}
function type(d) {
d.date = parseDate(d.date);
d.price = +d.price;
return d;
}
update();
.area {
fill: none;
stroke: #a2dced;
stroke-width: 2;
clip-path: url(#clip);
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
rect.selection {
fill: green;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>
Of course it's possible to add a tooltip in d3, there are a lot of examples and even a dedicated package for older versions.
You can choose to show a tooltip inside the SVG (as a rect with text) or outside, as a div. The benefit of outside is that the tooltip can overflow the SVG, the downside is that positioning can be more difficult, especially with scrolling.
I show a very simple implementation below, using a DIV tooltip. I positioned the .zoom rect behind the circles, so they would catch the mouse events instead, and added on mouseenter and mouseleave event listeners.
var data = [{
date: "10:30:00",
price: 36000
},
{
date: "11:00:20",
price: 40000
},
{
date: "12:00:00",
price: 38000
},
{
date: "14:20:00",
price: 50400
}
];
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 110,
left: 40
},
margin2 = {
top: 430,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
height2 = +svg.attr("height") - margin2.top - margin2.bottom;
var parseDate = d3.timeParse("%H:%M:%S"); //"%b %Y");
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var area = d3.line()
//.curve(d3.curveMonotoneX)
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.price);
});
var area2 = d3.line()
.curve(d3.curveMonotoneX)
.x(function(d) {
return x2(d.date);
})
.y(function(d) {
return y2(d.price);
});
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var tooltip = d3.select('body')
.append('div')
.attr('id', 'tooltip')
.style("transform", "translate(" + margin.left + "px," + margin.top + "px)")
.classed('hide', true);
function update() {
for (var k in data) {
type(data[k]);
}
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.price;
})]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
focus.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class", "circle")
.attr("r", 5)
.style("fill", 'orange')
.style("stroke", 'red')
.style("stroke-width", "2")
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.price);
})
.on("mouseenter", function(d) {
// Show the tooltip and position it correctly
tooltip.classed('hide', false)
.style('left', x(d.date).toString() + 'px')
.style('top', y(d.price).toString() + 'px')
.html("<p>Price: " + d.price + "</p>");
})
.on("mouseleave", function() {
tooltip.classed('hide', true);
});
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "circle")
.attr("r", 1)
.style("fill", 'blue')
.style("stroke", 'red')
.style("stroke-width", "2")
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.price);
});
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
// Insert the zoom rect *before* the circles, so the circles
// are drawn in front of the recrt
focus.insert("rect", "circle")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.call(zoom);
}
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.select(".area").attr("d", area);
focus.selectAll('.circle')
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.price);
});
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.select(".area").attr("d", area);
focus.selectAll('.circle')
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.price);
});
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
context.selectAll('.circle')
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.price);
});
}
function type(d) {
d.date = parseDate(d.date);
d.price = +d.price;
return d;
}
update();
.area {
fill: none;
stroke: #a2dced;
stroke-width: 2;
clip-path: url(#clip);
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
rect.selection {
fill: green;
}
#tooltip {
position: absolute;
border: solid 1px black;
background: white;
margin: 20px;
}
.hide {
opacity: 0;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>
Edited: added zooming.
I'm getting data from php function (just sql request) to js variable, so js variable is json data. I need to build zoomable d3 line chart based on this json.
Json variable looks like this:
[{temp: "5", created_at: "2017-03-19"},
{temp: "5", created_at: "2017-03-19"},
{temp: "26", created_at: "2018-04-20"}]
Error is: Error: <path> attribute d: Expected number, "MNaN,NaNLNaN,NaNL…"
Line_chart.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line); ///One error is on this line
context.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line2); ///Second error is on this line
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 110, left: 40},
margin2 = {top: 430, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
height2 = +svg.attr("height") - margin2.top - margin2.bottom;
var parseDate = d3.timeParse("%Y-%M-%D");
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var line = d3.line()
.x(function (d) { return x(d.Date); })
.y(function (d) { return y(d.Air_Temp); });
var line2 = d3.line()
.x(function (d) { return x2(d.Date); })
.y(function (d) { return y2(d.Air_Temp); });
var clip = svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("x", 0)
.attr("y", 0);
var Line_chart = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("clip-path", "url(#clip)");
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var data = jsonfile
x.domain(d3.extent(data, function(d) { return d.created_at; }));
y.domain([0, d3.max(data, function (d) { return d.temp; })]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
Line_chart.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
context.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
console.log(data);
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
Line_chart.select(".line").attr("d", line);
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
Line_chart.select(".line").attr("d", line);
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
function type(d) {
d.Date = parseDate(d.created_at);
d.Air_Temp = +d.temp;
return d;
}
jsonfile in d3.json is this json variable.
I have problem with dislaying data. I have x and y axis but no displayed data.
---------Reason of the error is incorrectly parsed data, still don't know where code goes wrong.-----------
I added: after var data = jsonfile
data.forEach(function(d){
d.created_at = parseDate(d.created_at);
d.temp = +d.temp;
});
And it worked. Graph builds using console. Don't why and where json strings parsed incorrectly.
I'm trying to add a brush filter to my line graph and I follow this example but, I get errors.
Error: attribute d: Expected number, "MNaN,NaNLNaN,NaNC…".
Error: attribute d: Expected number, "M0,NaNL0.1167307672…".
Here's my code:
var margin = {top: 20, right: 20, bottom: 110, left: 50},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
margin2 = { top: 430, right: 20, bottom: 30, left: 50 },
height2 = 500 - margin2.top - margin2.bottom;
var parseTime = d3.timeParse("%Y-%m-%d");
var x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
x2 = d3.scaleTime().range([0, width]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x)
yAxis = d3.axisLeft(y)
xAxis2 = d3.axisBottom(x2);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("#plot").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var plot = svg.append("g")
.attr("class", "plot")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var slider = svg.append("g")
.attr("class", "slider")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.number); });
d3.csv("dataset.csv", function(error, data) {
var brush = d3.brushX()
.extent([[0,0], [width,height2]])
.on("brush end", brushed);
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "date";
}));
data.forEach(function(d) {
d.date = parseTime(d.date);
});
var countries = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
date: d.date,
number: +d[name]
};
})
};
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([d3.min(countries, function(c) { return d3.min(c.values, function(v) { return v.number; });}),
d3.max(countries, function(c) { return d3.max(c.values, function(v) { return v.number; });})]);
x2.domain(x.domain());
y2.domain(y.domain());
plot.append("path")
.datum(countries)
.attr("class", "lines")
.attr("d", line);
plot.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
plot.append("g")
.attr("class", "y axis")
.call(yAxis);
var country = plot.append("g");
country.selectAll(".country")
.data(countries)
.enter().append("g")
.attr("class", "country")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
});
slider.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
slider.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
function brushed() {
var selection = d3.event.selection;
if (selection !== null) {
var e = d3.event.selection.map(x2.invert, x2);
var test = plot.selectAll(".country");
test.classed("selected", function (d) {
return e[0] <= d.date && d.date <= e[1];
})
plot.selectAll(".lines")
.attr("d", line(
data.filter(function (d) { return e[0] <= d.date && e[1] >= d.date; })
));
}
}
});
And this is a sample of my dataset(it's too big):
date,Albania,Andorra,Armenia,Austria,Azerbaijan,Belarus,Belgium,Bosnia And Herzegovina,Bulgaria
2005-01-01,3.446,3.671,-4.206,-2.775,1.137,-1.384,4.136,0.213,1.874
2005-02-01,2.257,2.695,-3.321,-4.726,0.463,-6.487,1.729,-1.492,-0.656
2005-03-01,7.271,6.856,2.788,0.994,5.689,-3.512,6.753,4.393,4.499
2005-04-01,11.485,10.308,10.032,7.286,11.847,8.084,10.391,10.248,10.36
2005-05-01,17.294,14.866,14.285,12.238,17.047,13.555,13.473,15.811,15.937
2005-06-01,20.165,20.264,17.723,15.518,20.635,15.768,17.995,18.599,18.293
2005-07-01,23.338,21.591,23.476,16.729,25.517,19.528,18.586,21.085,21.461
2005-08-01,21.928,19.788,22.649,14.81,24.131,17.452,16.905,18.998,21.001
2005-09-01,19.071,16.95,17.586,13.266,20.089,14.207,16.449,16.803,17.261
2005-10-01,13.412,13.817,10.424,8.319,13.177,7.485,13.622,11.447,11.173
2005-11-01,8.167,6.996,4.515,1.016,7.571,1.62,5.913,5.734,5.448
2005-12-01,5.131,1.919,0.874,-3.361,4.944,-2.699,2.782,1.786,2.728
2006-01-01,1.822,3.566,-5.34,-6.262,-1.433,-8.192,0.834,-0.999,-2.194
2006-02-01,4.094,4.108,-1.693,-3.009,2.31,-8.342,2.114,1.6,0.408
2006-03-01,7.838,8.464,4.863,0.098,7.183,-2.922,4.215,4.918,5.608
2006-04-01,12.538,11.472,9.841,7.314,11.629,7.324,9.084,11.314,11.099
2006-05-01,16.826,15.313,14.616,11.521,16.346,12.832,14.243,15.371,15.644
2006-06-01,20.294,19.307,21.928,15.63,24.331,17.092,17.391,18.668,19.27
2006-07-01,23.107,24.003,21.551,19.81,23.873,20.188,22.837,22.206,21.414
2006-08-01,22.6,19.595,25.291,13.915,26.676,18.018,16.55,19.208,22.229
2006-09-01,19.095,18.205,17.931,14.806,19.721,14.03,18.334,17.298,17.472
2006-10-01,14.799,15.087,12.034,9.613,14.837,8.517,13.763,13.252,12.843
2006-11-01,7.687,10.358,3.482,3.927,6.643,2.978,8.608,6.889,6.877
2006-12-01,5.023,4.534,-3.862,-0.032,0.431,2.382,5.17,3.13,2.741
It seems that it has a problem with: .attr("d", line), but I'm getting confused.
Thank you in advance!
I managed to find a way to get rid of those errors. If it helps someone else, I've changed this:
plot.append("path")
.datum(countries)
.attr("class", "lines")
.attr("d", line);
plot.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
plot.append("g")
.attr("class", "y axis")
.call(yAxis);
var country = plot.append("g");
country.selectAll(".country")
.data(countries)
.enter().append("g")
.attr("class", "country")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
});
To this:
plot.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
plot.append("g")
.attr("class", "y axis")
.call(yAxis);
var country = plot.selectAll(".country")
.data(countries)
.enter().append("g")
.attr("class", "country");
country.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
});
Small problems are difficult to identify. I am using example to create a linechart using d3 v4. I'm using d3.line() rather than d3.area(). Also drawing circles on the line peak points. Things are working fine.
Problem is with the drawn circles. They are not moving to the correct position when i'm zooming the graph.
Here is the complete code:
.area {
fill: none;
stroke: #a2dced;
stroke-width: 2;
clip-path: url(#clip);
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
rect.selection
{
fill:green;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>
<script>
var data = [{date: "10:30:00", price: 36000},
{date: "11:00:20", price: 40000},
{date: "12:00:00", price: 38000},
{date: "14:20:00", price: 50400}];
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 110, left: 40},
margin2 = {top: 430, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
height2 = +svg.attr("height") - margin2.top - margin2.bottom;
var parseDate = d3.timeParse("%H:%M:%S");//"%b %Y");
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var area = d3.line()
//.curve(d3.curveMonotoneX)
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.price);
});
var area2 = d3.line()
.curve(d3.curveMonotoneX)
.x(function (d) {
return x2(d.date);
})
.y(function (d) {
return y2(d.price);
});
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
function update(){
for(var k in data)
{
type(data[k]);
}
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.price; })]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
focus.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class","circle")
.attr("r", 5)
.style("fill", 'orange')
.style("stroke", 'red')
.style("stroke-width", "2")
.attr("cx", function(d) { return x(d.date) })
.attr("cy", function(d) { return y(d.price); });
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class","circle")
.attr("r", 1)
.style("fill", 'blue')
.style("stroke", 'red')
.style("stroke-width", "2")
.attr("cx", function(d) { return x(d.date) })
.attr("cy", function(d) { return y(d.price); });
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
}
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.select(".area").attr("d", area);
focus.selectAll('.circle').attr("transform", function(d) {
return "translate(" + x(d.date) + "," + y(d.price) + ")";
});
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.select(".area").attr("d", area);
focus.selectAll('.circle').attr("transform", function(d) {
return "translate(" + x(d.date) + "," + y(d.price) + ")";
});
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
context.selectAll('.circle').attr("transform", function(d) {
return "translate(" + x2(d.date) + "," + y2(d.price) + ")";
});
}
function type(d) {
d.date = parseDate(d.date);
d.price = +d.price;
return d;
}
update();
</script>
JSFiddle link.
I updated you jsfiddle by replacing all transformattributes by changing it to cx and cy, e.g.:
before
focus.selectAll('.circle').attr("transform", function(d) {
return "translate(" + x(d.date) + "," + y(d.price) + ")";
});
after
focus.selectAll('.circle')
.attr("cx", function(d) { return x(d.date) })
.attr("cy", function(d) { return y(d.price); });
Otherwise, you will translate with your other coordinates, which might lead to hard to find problems
clipPath in a d3-project don't clips: the graph overflows the chart window on the left.
I followed several examples available online, but I don't see, why my clipPath don't works.
Working example can be found here: https://phinetwork.ch/wolf/dynamicgraph.html
Code:
<style>
.area {
fill: darkorange;
}
.bgrect {
background-color:#999;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
background-color:grey;
}
.grid line{
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid text {
display:none;
}
.grid path, .xgrid path{
stroke-width: 0;
}
</style>
<svg width="900" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 0, bottom: 110, left: 40},
margin2 = {top: 430, right: 0, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
height2 = +svg.attr("height") - margin2.top - margin2.bottom;
var parseDate = d3.timeParse("%Y-%m-%d");
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x)
.ticks(10),
yAxis = d3.axisLeft(y)
.ticks(7),
xgAxis = d3.axisBottom(x)
.ticks(10)
.tickSize(-height),
ygAxis = d3.axisLeft(x)
.ticks(10)
.tickSize(-width - margin.left),
xAxis2 = d3.axisBottom(x2);
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var area = d3.area()
.curve(d3.curveMonotoneX)
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.rz); });
var area2 = d3.area()
.curve(d3.curveMonotoneX)
.x(function(d) { return x2(d.date); })
.y0(height2)
.y1(function(d) { return y2(d.rz); });
//svg.append("defs").append("clipPath")
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x", 40)
.attr("y", 0)
.attr("width", width-margin.left)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
d3.csv("files/institute/testdat.csv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.rz; })]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("rect")
.attr("class", "bgrect")
.attr("width", width)
.attr("height", height)
//.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("clip-path", "url(#clip)")
.attr("d", area);
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis grid")
.attr("transform", "translate(0," + height + ")")
.call(xgAxis);
focus.append("g")
.attr("class", "axis grid")
.attr("transform", "translate(0," + -margin.top + ")")
.call(ygAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
svg.append("rect")
.attr("class", "zoom")
.attr("width", width - margin.left)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
});
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.select(".area").attr("d", area);
focus.select(".axis--x").call(xAxis);
focus.select(".grid").call(xgAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.select(".area").attr("d", area);
focus.select(".axis--x").call(xAxis);
focus.select(".grid").call(xgAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
function type(d) {
d.date = parseDate(d.date);
d.rz = +d.rz;
return d;
}
</script>
Any suggestions for this problem?