block by d3noob 2e38c9aaca10fc6deb6498bd3723130d

Variable gradient test

Full Screen

A standard line chart with the addition of a gradient to show the range of values at each point. (In this case the range is ± 2 standard deviations.) Because the center point of the gradient varies with position on the x-axis, a single SVG area is not sufficient. Instead, the code creates a separate area (referred to as a slice) for each data point.

This was the best example I could find for demonstrating a gradient that varied over an irregular line.

forked from sathomas‘s block: Variable gradient test

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <title>Line chart with gradient range</title>
    <link href='//fonts.googleapis.com/css?family=Varela' rel='stylesheet'
          type='text/css'>
    <style>
        body { font-family: Varela,sans-serif; }
    </style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>

    <!--
        Because of cross-origin restrictions, we have to use
        a hack to load our dataset.
    -->
    <script src='atlweather.js'></script>

    <script>

        // Convenience functions that provide parameters for
        // the chart. In most cases these could be defined as
        // CSS rules, but for this particular implementation
        // we're avoiding CSS so that we can easily extract
        // the SVG into a presentation.

        var color = "#007979";

        // Define the dimensions of the visualization.
        var margin = {top: 80, right: 50, bottom: 50, left: 50},
            width = 636 - margin.left - margin.right,
            height = 436 - margin.top - margin.bottom;

        // Since this is a line chart, it graphs x- and y-values.
        // Define scales for each. Both scales span the size of the
        // chart. The x-scale is time-based (we're assuming months)
        // and the y-scale is linear. Note that the y-scale
        // ranges from `height` to 0 (opposite of what might be
        // expected) because the SVG coordinate system places a
        // y-value of `0` at the _top_ of the container.

        // At this point we don't know the domain for either of
        // the x- or y-values since that depends on the data
        // itself (which we'll retrieve in a moment) so we only
        // define the type of each scale and its range. We'll
        // add a definition of the domain after we retrieve the
        // actual data.
        var x = d3.time.scale()
            .range([0, width]);

        var y = d3.scale.linear()
            .range([height, 0]);

        // Define the axes for both x- and y-values. For the
        // x-axis, we specify a format for the tick labels
        // (just the month abbreviation).
        var xAxis = d3.svg.axis()
            .scale(x)
            .tickSize(0, 0, -height)
            .tickPadding(10)
            .tickFormat(d3.time.format("%b"))
            .orient("bottom");

        // For the y-axis we add grid lines by specifying a
        // negative value for the major tick mark size. We
        // set the size of the grid lines to be the entire
        // width of the graph.
        var yAxis = d3.svg.axis()
            .scale(y)
            .tickSize(-width, 0, -width)
            .tickPadding(10)
            .orient("left");

        // Define a convenience function to create a line on
        // the chart. The line's x-values are dates and the
        // y-values are the temperature values. The result
        // of this statement is that `line` will be a
        // function that, when passed a selection with an
        // associated array of data points, returns an SVG
        // path whose coordinates match the x- and y-scales
        // of the chart.
        var line = d3.svg.line()
            .x(function(d) { return x(d.date); })
            .y(function(d) { return y(d.temp); });

        // A similar convenience function generates an SVG
        // area. Our area is two standard deviations above
        // and below the average temperature.
        var area = d3.svg.area()
            .x(function(d) { return x(d.date); })
            .y0(function(d) { return y(d.temp - 2*d.stdv); })
            .y1(function(d) { return y(d.temp + 2*d.stdv); });

        // Create the SVG container for the visualization and
        // define its dimensions. Within that container, add a
        // group element (`<g>`) that can be transformed via
        // a translation to account for the margins.
        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 + ")");

        // Add the gradient definition to the SVG element.
        svg.append("linearGradient")
            .attr("id", "temperature-gradient")
            .attr("x1", 0).attr("y1", 0)
            .attr("x2", 0).attr("y2", "100%")
          .selectAll("stop")
            .data([
              {offset: "0%", color: "yellow"},
              {offset: "100%", color: "black"}
            ])
          .enter().append("stop")
            .attr("offset", function(d) { return d.offset; })
            .attr("stop-color", function(d) { return d.color; });


        // Define a convenience function to calculate the
        // path for a slice of the data.
        var slice = function(d,i) {
            var date = i ? dataset[i-1].date : d.date,
                temp = i ? dataset[i-1].temp : d.temp,
                stdv = i ? dataset[i-1].stdv : d.stdv,
                x0 = x(date)
                x1 = x(d.date),
                y0min = y(temp - 2*stdv),
                y0max = y(temp + 2*stdv),
                y1min = y(d.temp - 2*d.stdv),
                y1max = y(d.temp + 2*d.stdv);
            return "M" + x0 + "," + y0min +
                   "L" + x0 + "," + y0max +
                   "L" + x1 + "," + y1max +
                   "L" + x1 + "," + y1min +
                   "L" + x0 + "," + y0min;
        }

        // Convert the data into a more "understandable"
        // JavaScript object.
        dataset = dataset.map(function(d,i) {
            var dt = +d["DATE"],  // "20100101"
                yr = Math.floor(dt/10000),
                mn = Math.floor((dt%10000)/100) - 1,
                dy = ((dt%10000)%100),
                date = new Date(yr,mn,dy),
                temp = (+d["DLY-TAVG-NORMAL"])/10,
                stdv = (+d["DLY-TAVG-STDDEV"])/10;
            return {
                "date":  date,
                "temp":  (+d["DLY-TAVG-NORMAL"])/10,
                "stdv":  (+d["DLY-TAVG-STDDEV"])/10,
            };
        });

        // Now that we have the data, we can calculate
        // the domains for our x- and y-values. The x-values
        // are a little tricky because we want to add additional
        // space before and after the data. We start by getting
        // the extent of the data, and then extending that range
        // 16 days before the first date and 15 days after the
        // last date.
        var xMin = dataset[0].date,
            xMax = dataset[dataset.length-1].date;
        x.domain([d3.time.day.offset(xMin,-0),
                  d3.time.day.offset(xMax,0)]);

        // For the y-values, we want the chart to show the minimum
        // and maximum values from all the datasets.
        var yMin = d3.min(dataset, function(d) {
            return d.temp - 2*d.stdv;
        });
        var yMax = d3.max(dataset, function(d) {
            return d.temp + 2*d.stdv;
        });

        // The `.nice()` function gives the domain nice
        // rounded limits.
        y.domain([yMin, yMax]).nice();

        // Now that the domains are defined, we can determine
        // the width of each x-value. We'll need this to
        // create the area paths corresponding to each value's
        // range.
        var xDelta = x(dataset[1].date) - x(dataset[0].date);

        // With the domains defined, we also have enough
        // information to complete the axes. We position
        // the x-axis by translating it below the chart.
        svg.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + height + ")")
            .call(xAxis);

        // For the y-axis, we add a label.
        svg.append("g")
            .attr("class", "y axis")
            .call(yAxis);

        // Style the axes. As with other styles, these
        // could be more easily defined in CSS. For this
        // particular code, though, we're avoiding CSS
        // to make it easy to extract the resulting SVG
        // and paste it into a presentation.
        svg.selectAll(".axis line, .axis path")
            .attr("fill", "none")
            .attr("stroke", "#bbbbbb")
            .attr("stroke-width", "2px")
            .attr("shape-rendering", "crispEdges");

        svg.selectAll(".axis text")
            .attr("font-size", "14");

        svg.selectAll(".x.axis text")
            .attr("dx", "18");

        svg.selectAll(".axis .tick line")
            .attr("stroke", "#f0f0f0")
            .attr("stroke-width", "1");

        // Add slices for each data point (except the
        // first). These slices will show the range of
        // values.
        svg.selectAll(".slice.dataset")
            .data(dataset)
          .enter().append("path")
            .attr("class", "slice dataset")
            .attr("fill", "url(#temperature-gradient)")
            .attr("fill-opacity", "0.4")
            .attr("stroke", "none")
            .attr("d", slice);

        // Graph the dataset as a single line
        svg.append("path")
            .datum(dataset)
            .attr("class", "line dataset")
            .attr("fill", "none")
            .attr("stroke", color)
            .attr("stroke-width", "2")
            .attr("d", line);

        // Chart decoration. Once more we're avoiding
        // CSS for styling, but usually that would be
        // a better approach.
        d3.select("svg").append("text")
            .attr("transform", "translate(" +
                (margin.left + width/2) + ",20)")
            .attr("class", "title")
            .attr("font-size", "20")
            .attr("text-anchor", "middle")
            .text("Average Daily Temperature - Atlanta");

        d3.select("svg").append("text")
            .attr("transform", "translate(" +
                (margin.left + width/2) + ",44)")
            .attr("class", "subtitle")
            .attr("font-size", "15")
            .attr("text-anchor", "middle")
            .text("Source: www.noaa.gov");

    </script>
</body>
</html>

thumbnail.lnk

http://jsdatav.is/img/thumbnails/weather.png