block by curran fca58d54f94230a03a348891e866184c

Persons of Concern StreamGraph by Destination

Full Screen

A StreamGraph visualization of the total number of persons of concern (including Asylum-seekers, Internally displaced persons, Persons in IDP-like situation, Others of concern, Returned IDPs, Refugees (incl. refugee-like situations), Returnees, Persons in Refugee-like situation, and Stateless) grouped by Country / territory of asylum/residence. Excludes countries with relatively low counts. Data from UNHCR Population Statistics.

Draws from Labeled Streamgraph and Syrian Refugees by Settlement Type.

forked from curran‘s block: Refugees Streamgraph

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <script src="https://unpkg.com/d3@4.10.0"></script>
    <script src="https://unpkg.com/d3-area-label@1.2.0"></script>
    <title>Refugees Streamgraph</title>
    <style>
      body {
        margin: 0px;
      }
      .area-label {
        font-family: sans-serif;
        fill-opacity: 0.7;
        fill: white;
      }
      path:hover {
        fill-opacity: 1;
        fill:black;
      }
      path {
        fill-opacity: 0.8;
        stroke-width: 0.5;
      }
      text {
        pointer-events: none;
      }
      .axis--major .tick text, .legend text, .tooltip text {
        fill: #585858;
        font-family: sans-serif;
        font-size: 16pt;
      }
      .axis--minor .tick text {
        display: none;
      }
      .axis--major .tick line{
        stroke: #ddd;
        stroke-width: 2px;
      }
      .axis--minor .tick line{
        stroke: #eee;
      }
      .axis .domain {
        display: none;
      }
    </style>
  </head>
  <body>
    <svg width="960" height="500"></svg>
    <script>
      // Find the min and max year, then give the
      // full range of years between them.
      function computeYears (rawData) {
        var allYearsSet = d3.set();
        rawData.forEach(function (d) {
          d.values.forEach(function (d) {
            allYearsSet.add(d.key);
          });
        });
        var yearsExtent = d3.extent(allYearsSet
          .values()
          .map(function (yearStr) {
            return +yearStr;
          }));
        return d3
          .range(yearsExtent[0], yearsExtent[1] + 1)
          .map(function (year) {
            return new Date(year + "");
          });
      }

      var bisectDate = d3.bisector(function (d) {
        return d.date;
      }).left;

      function getInterpolatedValue (values, date, value){
        const i = bisectDate(values, date, 0, values.length - 1);
        if (i > 0) {
          const a = values[i - 1];
          const b = values[i];
          const t = (date - a.date) / (b.date - a.date);
          return value(a) * (1 - t) + value(b) * t;
        }
        return value(values[i]);
      }

      // Interpolate values, create data structure
      // for d3.stack.
      function interpolateValues (years, rawData) {
        var value = function (d) {
          return d.value;
        };
        return years.map(function (date) {

          // Create a new row object with the date.
          var row = {
            date: date 
          };

          // Assign values to the new row object for each key.
          // Value for `key` here will be country name.
          rawData.forEach(function (d){
            row[d.key] = getInterpolatedValue(d.values, date, value);
          });

          return row;
        });
      }

      d3.json('sumByCountryByYear.json', function (rawData) {

        // Parse dates, extract keys.
        var keys = rawData
          .filter(function (d) {
            var sum = d3.sum(d.values, function (d){ return d.value; });
            return sum > 1000000;
          })
          .map(function (d) {
            d.values.forEach(function (d) {
              d.date = new Date(d.key);
            });
            return d.key;
          });

        // Compute interpolated values for all years.
        var data = interpolateValues(computeYears(rawData), rawData);

        render(data, keys);
      });

      var margin = { top: 0, bottom: 30, left: 0, right: 30 };

      var svg = d3.select('svg');
      var width = +svg.attr('width');
      var height = +svg.attr('height');
      
      var g = svg.append('g')
          .attr('transform', `translate(${margin.left},${margin.top})`);
      var xAxisG = g.append('g')
          .attr('class', 'axis');
      var xAxisMinorG = xAxisG.append('g')
          .attr('class', 'axis axis--minor');
      var xAxisMajorG = xAxisG.append('g')
          .attr('class', 'axis axis--major');
      var marksG = g.append('g');
      
      var stack = d3.stack()
        .offset(d3.stackOffsetWiggle)
        .order(d3.stackOrderInsideOut)
      ;
      var xValue = function (d) { return d.date; };
      var xScale = d3.scaleTime();
      var yScale = d3.scaleLinear();
      var colorScale = d3.scaleOrdinal()
        .range(d3.schemeCategory10);
      
      var xAxisMajor = d3.axisBottom().scale(xScale);
      var xAxisMinor = d3.axisBottom().scale(xScale).ticks(50);
      
      var area = d3.area()
        .x(d => xScale(xValue(d.data)))
        .y0(d => yScale(d[0]))
        .y1(d => yScale(d[1]))
        .curve(d3.curveBasis);
      
      // Render StreamGraph
      function render(data, keys) {
        stack.keys(keys);
        var stacked = stack(data);
        
        var innerWidth = width - margin.right - margin.left;
        var innerHeight = height - margin.top - margin.bottom;

        xScale
          .domain(d3.extent(data, xValue))
          .range([0, innerWidth]);

        yScale
          .domain([
            d3.min(stacked, function (series) {
              return d3.min(series, function (d) { return d[0]; });
            }),
            d3.max(stacked, function (series) {
              return d3.max(series, function (d) { return d[1]; });
            })
          ])
          .range([innerHeight, 0]);
        
        colorScale.domain(d3.range(keys.length));
        
        var paths = marksG.selectAll('path').data(stacked);
        var pathsEnter = paths
          .enter().append('path');
        pathsEnter.merge(paths)
            .attr('fill', function (d) { return colorScale(d.index); })
            .attr('stroke', function (d) { return colorScale(d.index); })
            .attr('d', area);
        
        paths.select('title')
          .merge(pathsEnter.append('title'))
            .text(function (d) { return d.key; })

        var labels = marksG.selectAll('text').data(stacked)
        labels
          .enter().append('text')
            .attr('class', 'area-label')
          .merge(labels)
            .text(function (d) { return d.key; })
            .attr('transform', d3.areaLabel(area).interpolateResolution(1000));
        
        xAxisMajor.tickSize(-innerHeight);
        xAxisMinor.tickSize(-innerHeight);
        
        xAxisG.attr('transform', `translate(0,${innerHeight})`);
        xAxisMajorG.call(xAxisMajor);
        xAxisMinorG.call(xAxisMinor);
      }
    </script>
  </body>
</html>

fetchData.sh

wget http://popstats.unhcr.org/en/time_series.csv

package.json

{
  "name": "refugeestreamgraph",
  "author": "Curran Kelleher",
  "license": "MIT",
  "dependencies": {
    "d3-array": "^1.2.0",
    "d3-collection": "^1.0.4",
    "d3-dsv": "^1.0.5"
  }
}

process.js

var fs = require('fs'),
    d3 = Object.assign(
      require('d3-dsv'),
      require('d3-collection'),
      require('d3-array')
    ),
    inputFile = 'time_series.csv',
    outputFile = 'sumByCountryByYear.json',
    csvString = fs.readFileSync(inputFile, 'utf8'),
    csvData = d3.csvParse(csvString).map(function (d) {
      d.Value = +d.Value;
      //console.log(d['Population type']);
      return d;
    }),
    nested = d3.nest()
      .key(function (d) { return d['Country / territory of asylum/residence']; })
      .key(function (d) { return d.Year; })
      .rollup(function(values) {
        return d3.sum(values, function(d) {
          return d.Value;
        })
      })
      .entries(csvData),
    outputJSON = JSON.stringify(nested, null, 2);

fs.writeFileSync(outputFile, outputJSON);