Will Luce
Will Luce

Reputation: 1841

Fill color under a line graph in D3

I'm attempting to make a gradient chart with the fill color under the line graph based on the gradient calculated by the average of every 100m for a 1500m course. Here's what I'm starting with, but I'm pretty lost as to where to go from here. I've done a bit of research and I'm struggling to put the "fill" concepts together.

What I have here will fill the line color (an extension of this) with a color based on a function, but I'd like to change this to the fill color under the line graph.

var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 1250 - margin.left - margin.right,
    height = 150 - margin.top - margin.bottom;

// Define X and Y Axis
var x = d3.scale.linear()
    .range([0, width]);
var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickSize(-height, 0)
    .tickPadding(6);
var y = d3.scale.linear()
    .range([height, 0]);
var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")

var area = d3.svg.area()
    .x(function(d) { return x(d.distance); })
    .y0(height)
    .y1(function(d) { return y(d.elevation); });

var line = d3.svg.line()
    .interpolate("basis")
    .x(function(d) { return x(d.distance); })
    .y(function(d) { return y(d.elevation); });

var svg = d3.select("#zoom_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 + ")");

var zoom = d3.behavior.zoom()
    .on("zoom", draw);

svg.append("clipPath")
    .attr("id", "clip")
  .append("rect")
    .attr("x", x(0))
    .attr("y", y(1))
    .attr("width", x(1) - x(0))
    .attr("height", y(0) - y(1));

// Append Y Axis
svg.append("g")
    .attr("class", "y axis")
    .call(yAxis)
  .append("text")
    .attr("transform", "rotate(-90)")
    .style("text-anchor", "end")
    .text("Elevation (m)");

// Append X Axis
svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis)
  .append("text")
    .attr("x", 1180)
    .attr("dx", ".71em")
    .style("text-anchor", "end")
    .text("Distance (km)");

// Define the path
svg.append("path")
    .attr("class", "line")
    .attr("clip-path", "url(#clip)");

svg.append("rect")
    .attr("class", "pane")
    .attr("width", width)
    .attr("height", height)
    .call(zoom);

// Define polyline options
// http://leafletjs.com/reference.html#polyline
var polyline_options = {
    color: '#000'
};

var line_points = [];

d3.csv("Gradient Dummy File.csv", function(error, data) {
  data.forEach(function(d) {
    d.distance = +d.distance;
    d.elevation = +d.elevation;
    d.latitude = +d.latitude;
    d.longitude = +d.longitude;
    line_points.push([d.latitude, d.longitude]);
  });

    x.domain(d3.extent(data, function(d) { return d.distance; }));
    y.domain([0, d3.max(data, function(d) { return d.elevation; })]);
    zoom.x(x);

    svg.append("linearGradient")                
        .attr("id", "line-gradient")            
        .attr("gradientUnits", "userSpaceOnUse")    
        .attr("x1", 0).attr("y1", y(0))         
        .attr("x2", 0).attr("y2", y(100))       
    .selectAll("stop")                      
        .data([                             
            {offset: "0%", color: "red"},       
            {offset: "4%", color: "red"},   
            {offset: "4%", color: "black"},     
            {offset: "6%", color: "black"},     
            {offset: "6%", color: "lawngreen"}, 
            {offset: "10%", color: "lawngreen"} 
        ])                  
    .enter().append("stop")         
        .attr("offset", function(d) { return d.offset; })   
        .attr("stop-color", function(d) { return d.color; });

    svg.select("path.area").data([data]);
    svg.select("path.line").data([data]);
    draw();

});

function draw() {
  svg.select("g.x.axis").call(xAxis);
  svg.select("g.y.axis").call(yAxis);
  svg.select("path.area").attr("d", area);
  svg.select("path.line").attr("d", line);
}

The css to go with this looks like this

body {
    font: 10px sans-serif;
    margin: 0px auto;
}

#map-container {
  width: 100%;
  height: 75%;
  margin: 0px auto;
  float: left;
}

#map { position:absolute; top:0; bottom:0; width:100%; }

#chart-container {
  width: 100%;
  margin: 0px auto;
  float: left;
  clear: both;
}

h1 {
  font-family: Helvetica;
  font-size: 5em;
}

p {
  font-family: Helvetica;
  font-size: 2em;
}

.axis {
  shape-rendering: crispEdges;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  stroke-width: 1pt;
  shape-rendering: crispEdges;
}

.x.axis path {
  stroke: #000;
}

.x.axis line {
  stroke: #fff;
  stroke-opacity: .5;
}

.y.axis line {
  stroke: #ddd;
}

path.line {
  fill: none;
  stroke: url(#line-gradient);
  stroke-width: .5px;
}

path.area {
  fill: lightsteelblue;
  stroke-width: 0;
}

rect.pane {
  cursor: move;
  fill: none;
  pointer-events: all;
}

.dot {
  stroke: #000;
}

Upvotes: 1

Views: 6912

Answers (1)

Kaiido
Kaiido

Reputation: 136746

You could do like so:

  • Set the fill property of your path class to your gradient. Remove the x1, x2, y1, y2 attributes of the linear gradient, so it sets automatically to 0, 0, 100%, 0 .
  • While setting the d attribute, add a line to (0, 0), and another one to (maxX, 0) coordinates. This way, your path will act as a close shape to y(0).

CSS

.line {
fill: url(#line-gradient);
stroke: url(#line-gradient);
stroke-width: 2px;
}

JS

svg.append("linearGradient")                
    .attr("id", "line-gradient")            
    .attr("gradientUnits", "userSpaceOnUse")    


// Add the valueline path.
var maxX = x(d3.extent(data, function(d) { return d.date; })[1]);
svg.append("path")
    .attr("class", "line")
    .attr("d", ''+valueline(data)+"L0,"+y(0)+'L'+maxX+","+y(0));

Live Example

// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
    width = 600 - margin.left - margin.right,
    height = 270 - margin.top - margin.bottom;

// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;

// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);

// Define the axes
var xAxis = d3.svg.axis().scale(x)
    .orient("bottom").ticks(5);

var yAxis = d3.svg.axis().scale(y)
    .orient("left").ticks(5);

// Define the line
var valueline = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });
    
// Adds the svg canvas
var svg = d3.select("body")
    .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
    .append("g")
        .attr("transform", 
              "translate(" + margin.left + "," + margin.top + ")");

// Get the data
d3.csv("https://gist.githubusercontent.com/d3noob/3e72cafd95e1834f599b/raw/b9a688b0db58ce72db0cef5b581e92fd02aa1220/data.csv", function(error, data) {
    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, d3.max(data, function(d) { return d.close; })]);
    

    svg.append("linearGradient")                
        .attr("id", "line-gradient")            
        .attr("gradientUnits", "userSpaceOnUse")    


    .selectAll("stop")                      
        .data([                             
            {offset: "0%", color: "red"},       
            {offset: "40%", color: "red"},  
            {offset: "40%", color: "black"},        
            {offset: "62%", color: "black"},        
            {offset: "62%", color: "lawngreen"},    
            {offset: "90%", color: "lawngreen"} 
        ])                  
    .enter().append("stop")         
        .attr("offset", function(d) { return d.offset; })   
        .attr("stop-color", function(d) { return d.color; });

    // Add the valueline path.
    var maxX = x(d3.extent(data, function(d) { return d.date; })[1]);
    svg.append("path")
        .attr("class", "line")
        .attr("d", ''+valueline(data)+"L0,"+y(0)+'L'+maxX+","+y(0));

    // Add the X Axis
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    // Add the Y Axis
    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);

});
body { font: 12px Arial;}

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

.axis path,
.axis line {
    fill: none;
    stroke: grey;
    stroke-width: 1;
    shape-rendering: crispEdges;
}

.line {
    fill: url(#line-gradient);
    stroke: url(#line-gradient);
    stroke-width: 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Upvotes: 1

Related Questions