I am trying to create a simple multi-line chart using JSON data similar to the following:
[
{
sampleDate: "2014-04-14",
shortName: "PFOA",
pfcLevel: "0.3500000"
},
{
sampleDate: "2014-05-14",
shortName: "PFOA",
pfcLevel: "0.3200000"
},
{
sampleDate: "2014-04-14",
shortName: "PFOS",
pfcLevel: "2.5000000"
},
{
sampleDate: "2014-05-14",
shortName: "PFOS",
pfcLevel: "2.4000000"
}
]
I have basic X and Y axis showing, but the actual value lines are not displaying. Looking at the DOM the path element is not showing the d attribute.
<path class="line" style="stroke: green;"></path>
The code is below:
<script>
var data = <?php echo $wellSamples ?>;
console.log(data);
// 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("%Y-%m-%d").parse;
// Set the ranges
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");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { console.log(d.sampleDate); return x(d.sampleDate); })
.y(function(d) { console.log(d.pfcLevel); return y(d.pfcLevel); });
var svg = d3.select("#chart").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 + ")");
x.domain(d3.extent(data, function(d) { return parseDate(d.sampleDate); }));
y.domain(d3.extent(data, function(d) { return d.pfcLevel; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("PFC Level");
var pfc = svg.selectAll(".pfc")
.data(data)
.enter().append("g")
.attr("class", "pfc");
pfc.append("path")
.attr("class", "line")
.attr("d", line)
.style("stroke", "green");
</script>
Several problems here:
Your data-binding is invalid. You bind the data then call .attr("d", line), this would call the line function on each point. It needs awhole array -- .attr("d", line(data))
You've made no attempt to create more then one line from that data. I'm guessing you want a line per "shortName"? You need to nest the data.
Your line x accessor calls .x(function(d) { return x(d.sampleDate); }), d.sampleDate has never been converted to a date though, it's still a string.
Putting this all together:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
var data = [
{
sampleDate: "2014-04-14",
shortName: "PFOA",
pfcLevel: "0.3500000"
},
{
sampleDate: "2014-05-14",
shortName: "PFOA",
pfcLevel: "0.3200000"
},
{
sampleDate: "2014-04-14",
shortName: "PFOS",
pfcLevel: "2.5000000"
},
{
sampleDate: "2014-05-14",
shortName: "PFOS",
pfcLevel: "2.4000000"
}
];
// Parse the date / time
var parseDate = d3.time.format("%Y-%m-%d").parse;
// clean up data
data.forEach(function(d){
d.sampleDate = parseDate(d.sampleDate);
d.pfcLevel = +d.pfcLevel;
});
// nest data
var nested_data = d3.nest()
.key(function(d) { return d.shortName; })
.entries(data);
// 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;
// Set the ranges
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");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.sampleDate); })
.y(function(d) { return y(d.pfcLevel); });
var svg = d3.select("#chart").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 + ")");
x.domain(d3.extent(data, function(d) { return d.sampleDate; }));
y.domain(d3.extent(data, function(d) { return d.pfcLevel; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("PFC Level");
// now we bind to nested_data, an array of arrays
var pfc = svg.selectAll(".pfc")
.data(nested_data)
.enter()
.append("g")
.attr("class", "pfc");
pfc.append("path")
.attr("class", "line")
.attr("d", function(d){
// our inner array is d.values from the nesting
return line(d.values);
})
.style("stroke", "green");
</script>
</body>
</html>
Related
I need to draw axis grid lines only inside areas in Area Chart, written in D3 (version 4) .
Have any solutionss of this issue?
Use negative width and height for the axis tick length
And then use a CSS style to stroke the grid.
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.call(d3.axisLeft(y));
g.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.tickSize(-height)
.tickFormat("")
);
g.append("g")
.attr("class", "grid")
.call(d3.axisLeft(y)
.tickSize(-width)
.tickFormat("")
);
Edit
If it is inside the Area define a clipping path that equals the area and group the grid lines to this clipping path.
Using the example from https://www.mattlayman.com/blog/2015/d3js-area-chart/
var data = [
{ x: 0, y: 10, },
{ x: 1, y: 15, },
{ x: 2, y: 35, },
{ x: 3, y: 20, },
];
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 575 - margin.left - margin.right,
height = 350 - margin.top - margin.bottom;
var x = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return d.x; })])
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return d.y; })])
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x);
var yAxis = d3.axisLeft()
.scale(y);
var area = d3.area()
.x(function(d) { return x(d.x); })
.y0(height)
.y1(function(d) { return y(d.y); });
var svg = d3.select("svg#area")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("clipPath")
.attr("id", "area-clip")
.append("path")
.attr("d", area(data));
var grid = svg.append("g")
.attr("clip-path","url(#area-clip)");
grid.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(xAxis.tickSize(-height).tickFormat(""));
grid.append("g")
.attr("class", "grid")
.call(yAxis.tickSize(-width).tickFormat(""));
.area {fill:steelblue;}
.grid line {fill:none; stroke:red; stroke-width:1;}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg id="area"></svg>
I try to adapt the following script.
But unfortunately I can not match the data points to the legend on the x-axis?
How can I move the data points / the line to the right to match the legend on the x-axis?
Source is: http://mund-consulting.com/Blog/visualizing-web-page-views-using-d3js/
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script>
var margin = { top: 40, right: 20, bottom: 30, left: 40 },
width = 900,
height = 400;
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");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d) {
return "<strong>"+d.MonthName+": </strong>
<span style='color:black'>" + d.PageViews + "</span>";
})
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 + ")");
svg.call(tip);
d3.csv("mc_monthly_pageview_2014.csv", function (error, data) {
x.domain(data.map(function (d) { return d.MonthName; }));
y.domain([0, d3.max(data, function (d) { return d.PageViews; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Page Views");
var line = d3.svg.line()
.x(function (d) { return x(d.MonthName); })
.y(function (d) { return y(d.PageViews); });
svg.selectAll("path.line")
.data(data)
.enter()
.append("path")
.attr("class", "line")
.attr("d", line(data));
svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", function (d) { return x(d.MonthName); })
.attr("cy", function (d) { return y(d.PageViews); })
.attr("r", 4.5)
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
});
</script>
I found a solution.
Exchange
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
with
var x = d3.scale.ordinal()
.rangePoints([0, width]);
The codes are the following: I am trying to create two "div" .Each div has a picture. However, the pictures are collapsing together after all the texts. They do not stay in their individual div.
<div class="container">
<div class ="row">
<h1> Title </h1>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" >
var margin = {top: 20, right: 30, bottom: 30, left: 100},
width = 800 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var formato = d3.format("0.0");
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")
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(formato);
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 dataFile_1 = "data.csv"
d3.csv(dataFile_1, function(error1, data1) {
data1.forEach(function(d) {
d.petitionRate = +d.petitionRate;
});
x.domain(data1.map(function(d) { return d.state; }));
y.domain([0, d3.max(data1, function(d) { return d.petitionRate; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Participation rate");
svg.selectAll(".bar")
.data(data1)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.state); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.petitionRate); })
.attr("height", function(d) { return height - y(d.petitionRate)});
</div>
<div class ="row">
If I put a picture here as the first one. Both of them do not stay in their div.
</div>
</div>
Please help me understand what the problem is here.
Thanks.
This line:
var svg = d3.select("body").append("svg")
appends d3s svg to the end of the html body. If you want the svg in the
<div class ="row">
directly preceding your JavaScript code do this:
<div class ="row" id="visRow">
and then append to that div:
var svg = d3.select("#visRow").append("svg")
I have a problem... I am not able to include tooltips.. i've tried to do it with d3noobs tutorial and with much other.. But it doesn't work..
I hope anyone can help me or fullfill the code!
Thanks a lot!
<pre>
<!-- /.row -->
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-body" id="gr1">
<svg id="graph" viewBox="0 0 100% 100%" preserveAspectRatio="xMidYMid" width="1500" height="600" ></svg>
<script src="http://d3js.org/d3.v3.min.js"></script>
<div id="tooltip" class="hidden" style="left: 429px, top: 489.6px">
<p><strong><span id="city">Dar es Salaam</span></strong></p>
<p id="population">Population: 4 million</p>
</div>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 1300 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#FF4000", "#D7DF01", "#DF013A", "#fbf10d"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("#graph")
.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 + ")");
function make_x_axis() {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5)
}
function make_y_axis() {
return d3.svg.axis()
.scale(y)
.orient("left")
.ticks(20)
}
function mouseover(d) {
d3.select(this).style("fill", "orange");
var mousecoord = [0,0];
mousecoord = d3.mouse(this);
d3.select("#tooltip")
.style("left", mousecoord[0] + "px")
.style("top", mousecoord[1]-75 + "px");
d3.select("#city")
.text(d.city);
d3.select("#population")
.text(function () { return year + " population: " + comma(d["y"+year]); });
d3.select("#tooltip").classed("hidden", false);
};
d3.csv("groupeddatafordefaultdashboard.asp?3", function(error, data) {
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "State"; });
data.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.ages, function(d) { return d.value; }); })]);
svg.append("g")
.attr("class", "grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat("")
)
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Anzahl");
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
(blah blah)
.on("mouseover", mouseover)
.on("mouseout", mouseout);
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(20," + i * 50 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
</script>
</pre>
I am trying to create multiple series in d3 with different fill colors for the area in between the lines for each series. I have successfully gotten the first series in, and I am confident I can create multiple series, but I can't figure out how to append the fill color of just that series. I've tried to do something along the lines of: .attr("fill","red"); to append the area but this doesn't work. Here is my full code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.area {
fill: steelblue;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%H:%M:%S").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");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
var area = d3.svg.area()
.x(function(d) { return x(d.date); })
.y0(function(d) { return y(d.low); })
.y1(function(d) { return y(d.high); });
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 context = svg.append("g")
.attr('class','container')
d3.csv("data.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.high = +d.positive;
d.low = +d.positivelow;
});
x.domain(d3.extent(data, function(d) { return d.date; }));
<!--y.domain(d3.extent(data, function(d) { return d.close; }));-->
<!--y.domain([d3.min(data, function(d) { return d.low; }), d3.max(data, function(d) { return d.high; })]);-->
y.domain([1720,1780])
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
<!--(This line below doesn't work)-->
<!--.attr("fill","red");-->
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Some Y Title");
});
</script>