userlkjsflkdsvm userlkjsflkdsvm - 2 months ago 13
Javascript Question

Nested plot and line graph in d3

I have the correct plot graph with the correlating colors. However, I am supposed to connect each plot with a line. There are two groups of plots. So there will be two separate line groups (otherwise known as being nested). I'm not sure how to go about that.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3-legend/1.1.0/d3-legend.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3-tip/0.6.3/d3-tip.min.js"></script>
<style>
h1 { border-bottom: solid 3px #EBEBED; color: #545c61; font-size: 20px; padding-left: 35px; font-family: sans-serif; padding-bottom: 17px; font-weight: 500; margin-bottom: 37px; padding-top: 8px;}

circle{ fill:white; stroke-width: 3px;}

path { stroke: red; stroke-width: 2; fill: none; }

.axis text {
font-family: 'Open Sans', sans-serif;
font-size: 14pt;
}

.axis path, .axis line {
fill: none;
stroke: none;
shape-rendering: crispEdges;
}
.y.axis { stroke: #babfc5; }
.y.axis text{ font-weight:100; transform: translate(-24px,0px); }
.y.axis line { stroke: #EBEBED; stroke-width: 2; }
.x.axis { stroke: #8e8e8e; }
.x.axis text{ font-weight: 500; transform:translate(0px,14px)}


</style>
</head>
<body>
<h1>Example</h1>
<script>
var outerWidth = 1080;
var outerHeight = 330;
var margin = { left: 190, top: 30, right: 30, bottom: 40 };
var padding = 1.0;
var circleRadius = 8;
var xColumn = "month";
var yColumn = "amount";
var colorColumn = "monthv";
var yAxisTitlesOffset = { top: 10 }


var innerWidth = outerWidth - margin.left - margin.right;
var innerHeight = outerHeight - margin.top - margin.bottom;



var svg = d3.select("body").append("svg")
.attr("width", outerWidth)
.attr("height", outerHeight);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xAxisG = g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + innerHeight + ")");
var yAxisG = g.append("g")
.attr("class", "y axis");




var xScale = d3.scale.ordinal()
.rangeBands([0, innerWidth],padding);
var yScale = d3.scale.linear()
.range([innerHeight,0]);
var colorScale = d3.scale.ordinal()
.domain(["top", "bot"])
.range(["#43B365", "#DA5A60" ])




var xAxis = d3.svg.axis().scale(xScale).orient("bottom")
.outerTickSize(0); // Turn off the marks at the end of the axis.
var yAxis = d3.svg.axis().scale(yScale).orient("left")
.ticks(4)
.tickSize(-innerWidth)
.outerTickSize(0)
.tickFormat( function(d) { return "$" + d} );

var format = d3.format(",");
var formattedX = format(yAxis);



function render(data){

xScale.domain( data.map( function (d){ return d[xColumn]; }));
yScale.domain([0, 2000]);
//if this code is inserted in the max value instead of 2000 - it will show the max data value d3.max(data, function (d){ return d[yColumn]; })
xAxisG.call(xAxis);
yAxisG.call(yAxis);

svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));

//bind data
var circles = g.selectAll("circle").data(data);
//Enter
circles.enter().append("circle")
.attr("r", circleRadius);
//update
circles
.attr("cx", function (d){ return xScale(d[xColumn]); })
.attr("cy", function (d){ return yScale(d[yColumn]); })
.style("stroke", function (d){ return colorScale(d[colorColumn]); })
//exit
circles.exit().remove();
}




function type(d){
d.amount = +d.amount;
return d;
}

var data = [
{
"month": "Jan",
"monthv": "top",
"amount": 400
},
{
"month": "Jan",
"monthv": "bot",
"amount": 100
},
{
"month": "Feb",
"monthv": "top",
"amount": 800
},
{
"month": "Feb",
"monthv": "bot",
"amount": 250
},
{
"month": "Mar",
"monthv": "top",
"amount": 750
},
{
"month": "Mar",
"monthv": "bot",
"amount": 200
},
{
"month": "Apr",
"monthv": "top",
"amount": 850
},
{
"month": "Apr",
"monthv": "bot",
"amount": 250
},
{
"month": "May",
"monthv": "top",
"amount": 800
},
{
"month": "May",
"monthv": "bot",
"amount": 250
},
{
"month": "Jun",
"monthv": "top",
"amount": 850
},
{
"month": "Jun",
"monthv": "bot",
"amount": 250
},
{
"month": "Jul",
"monthv": "top",
"amount": 1000
},
{
"month": "Jul",
"monthv": "bot",
"amount": 300
},
{
"month": "Aug",
"monthv": "top",
"amount": 1050
},
{
"month": "Aug",
"monthv": "bot",
"amount": 300
},
{
"month": "Sep",
"monthv": "top",
"amount": 1000
},
{
"month": "Sep",
"monthv": "bot",
"amount": 300
},
{
"month": "Oct",
"monthv": "top",
"amount": 1200
},
{
"month": "Oct",
"monthv": "bot",
"amount": 300
},
{
"month": "Nov",
"monthv": "top",
"amount": 1100
},
{
"month": "Nov",
"monthv": "bot",
"amount": 250
},
{
"month": "Dec",
"monthv": "top",
"amount": 1250
},
{
"month": "Dec",
"monthv": "bot",
"amount": 250
}
];

render(data);
</script>
</body>
</html>


I would like something to look like this http://bl.ocks.org/d3noob/38744a17f9c0141bcd04 . However when I define the line in my code and it to the path, I am getting an error. Not sure how to go about this.

Answer

You forgot to define valueline, the path generator: d3.svg.line:

var valueline = d3.svg.line()
    .x(function(d) { return xScale(d[xColumn]); })
    .y(function(d) { return yScale(d[yColumn]); });

Note for the way your data is defined, you need to call the path generator twice filtering the data for the top line and for the bottom line.You could also prepare the data to have one object for each month with values for the top and bottom line:

g.append("path")
.attr("class", "line")
.attr("d", valueTopline(data.filter( e => e[colorColumn] === "top")))
.style("stroke", colorScale("top"));

g.append("path")
.attr("class", "line")
.attr("d", valueTopline(data.filter( e => e[colorColumn] === "bot")))
.style("stroke", colorScale("bot"));

You also need to append the paths to the group <g> instead of appending direct to the svg for the scales to match.

Here is the updated jsbin code: