Skip to content

Instantly share code, notes, and snippets.

@micahstubbs
Last active August 11, 2022 12:04
Show Gist options
  • Select an option

  • Save micahstubbs/c7f17dcbdc728e0d579d84e47c33dfa6 to your computer and use it in GitHub Desktop.

Select an option

Save micahstubbs/c7f17dcbdc728e0d579d84e47c33dfa6 to your computer and use it in GitHub Desktop.

Revisions

  1. micahstubbs revised this gist Jun 26, 2019. 2 changed files with 223 additions and 201 deletions.
    202 changes: 1 addition & 201 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -84,207 +84,7 @@
    <script src="d3-tip.js"></script>
    <script src='https://unpkg.com/[email protected]/dist/simple-statistics.min.js'></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.10.3/babel.min.js'></script>
    <script lang='babel' type='text/babel'>
    // configuration
    const colorVariable = 'population';
    const geoIDVariable = 'id';
    const format = d3.format(',');

    // Set tooltips
    const tip = d3.tip()
    .attr('class', 'd3-tip')
    .offset([-10, 0])
    .html(d => `<strong>Country: </strong><span class='details'>${d.properties.name}<br></span><strong>Population: </strong><span class='details'>${format(d[colorVariable])}</span>`);

    tip.direction(function(d) {
    if (d.properties.name === 'Antarctica') return 'n';
    // Americas
    if (d.properties.name === 'Greenland') return 's';
    if (d.properties.name === 'Canada') return 'e';
    if (d.properties.name === 'USA') return 'e';
    if (d.properties.name === 'Mexico') return 'e';
    // Europe
    if (d.properties.name === 'Iceland') return 's';
    if (d.properties.name === 'Norway') return 's';
    if (d.properties.name === 'Sweden') return 's';
    if (d.properties.name === 'Finland') return 's';
    if (d.properties.name === 'Russia') return 'w';
    // Asia
    if (d.properties.name === 'China') return 'w';
    if (d.properties.name === 'Japan') return 's';
    // Oceania
    if (d.properties.name === 'Indonesia') return 'w';
    if (d.properties.name === 'Papua New Guinea') return 'w';
    if (d.properties.name === 'Australia') return 'w';
    if (d.properties.name === 'New Zealand') return 'w';
    // otherwise if not specified
    return 'n';
    })

    tip.offset(function(d) { // [top, left]
    if (d.properties.name === 'Antarctica') return [0, 0];
    // Americas
    if (d.properties.name === 'Greenland') return [10, -10];
    if (d.properties.name === 'Canada') return [24, -28];
    if (d.properties.name === 'USA') return [-5, 8];
    if (d.properties.name === 'Mexico') return [12, 10];
    if (d.properties.name === 'Chile') return [0, -15];
    // Europe
    if (d.properties.name === 'Iceland') return [15, 0];
    if (d.properties.name === 'Norway') return [10, -28];
    if (d.properties.name === 'Sweden') return [10, -8];
    if (d.properties.name === 'Finland') return [10, 0];
    if (d.properties.name === 'France') return [-9, 66];
    if (d.properties.name === 'Italy') return [-8, -6];
    if (d.properties.name === 'Russia') return [5, 385];
    // Africa
    if (d.properties.name === 'Madagascar') return [-10, 10];
    // Asia
    if (d.properties.name === 'China') return [-16, -8];
    if (d.properties.name === 'Mongolia') return [-5, 0];
    if (d.properties.name === 'Pakistan') return [-10, 13];
    if (d.properties.name === 'India') return [-11, -18];
    if (d.properties.name === 'Nepal') return [-8, 1];
    if (d.properties.name === 'Myanmar') return [-12, 0];
    if (d.properties.name === 'Laos') return [-12, -8];
    if (d.properties.name === 'Vietnam') return [-12, -4];
    if (d.properties.name === 'Japan') return [5, 5];
    // Oceania
    if (d.properties.name === 'Indonesia') return [0, -5];
    if (d.properties.name === 'Papua New Guinea') return [-5, -10];
    if (d.properties.name === 'Australia') return [-15, 0];
    if (d.properties.name === 'New Zealand') return [-15, 0];
    // otherwise if not specified
    return [-10, 0];
    })

    d3.select('body')
    .style('overflow', 'hidden');

    const parentWidth = d3.select('body').node().getBoundingClientRect().width;
    const margin = {top: 0, right: 0, bottom: 0, left: 0};
    const width = 960 - margin.left - margin.right;
    const height = 500 - margin.top - margin.bottom;

    const color = d3.scaleQuantile()
    .range([
    'rgb(247,251,255)',
    'rgb(222,235,247)',
    'rgb(198,219,239)',
    'rgb(158,202,225)',
    'rgb(107,174,214)',
    'rgb(66,146,198)',
    'rgb(33,113,181)',
    'rgb(8,81,156)',
    'rgb(8,48,107)',
    'rgb(3,19,43)'
    ]);


    const svg = d3.select('body')
    .append('svg')
    .attr('width', width)
    .attr('height', height)
    .append('g')
    .attr('class', 'map');

    const projection = d3.geoRobinson()
    .scale(148)
    .rotate([352, 0, 0])
    .translate( [width / 2, height / 2]);

    const path = d3.geoPath().projection(projection);

    svg.call(tip);

    queue()
    .defer(d3.json, 'world_countries.json')
    .defer(d3.tsv, 'world_population.tsv')
    .await(ready);

    function ready(error, geography, data) {
    data.forEach(d => {
    d[colorVariable] = Number(d[colorVariable].replace(',', ''));
    })

    const colorVariableValueByID = {};

    data.forEach(d => { colorVariableValueByID[d[geoIDVariable]] = d[colorVariable]; });
    geography.features.forEach(d => { d[colorVariable] = colorVariableValueByID[d.id] });

    // calculate ckmeans clusters
    // then use the max value of each cluster
    // as a break
    const numberOfClasses = color.range().length - 1;
    const ckmeansClusters = ss.ckmeans(data.map(d => d[colorVariable]), numberOfClasses);
    const ckmeansBreaks = ckmeansClusters.map(d => d3.min(d));
    console.log('numberOfClasses', numberOfClasses);
    console.log('ckmeansClusters', ckmeansClusters);
    console.log('ckmeansBreaks', ckmeansBreaks);

    // set the domain of the color scale based on our data
    color
    .domain(ckmeansBreaks);
    //
    // .domain(jenksNaturalBreaks)
    //
    // .domain(d3.extent(data, d => d[colorVariable]));
    //
    // .domain([
    // 10000,
    // 100000,
    // 500000,
    // 1000000,
    // 5000000,
    // 10000000,
    // 50000000,
    // 100000000,
    // 500000000,
    // 1500000000
    // ]);

    svg.append('g')
    .attr('class', 'countries')
    .selectAll('path')
    .data(geography.features)
    .enter().append('path')
    .attr('d', path)
    .style('fill', d => {
    if (typeof colorVariableValueByID[d.id] !== 'undefined') {
    return color(colorVariableValueByID[d.id])
    }
    return 'white'
    })
    .style('fill-opacity',0.8)
    .style('stroke', d => {
    if (d[colorVariable] !== 0) {
    return 'white';
    }
    return 'lightgray';
    })
    .style('stroke-width', 1)
    .style('stroke-opacity', 0.5)
    // tooltips
    .on('mouseover',function(d){
    tip.show(d);
    d3.select(this)
    .style('fill-opacity', 1)
    .style('stroke-opacity', 1)
    .style('stroke-width', 2)
    })
    .on('mouseout', function(d){
    tip.hide(d);
    d3.select(this)
    .style('fill-opacity', 0.8)
    .style('stroke-opacity', 0.5)
    .style('stroke-width', 1)
    });

    svg.append('path')
    .datum(topojson.mesh(geography.features, (a, b) => a.id !== b.id))
    .attr('class', 'names')
    .attr('d', path);
    }
    <script src='./index.js' lang='babel' type='text/babel'>
    </script>
    </body>
    </html>
    222 changes: 222 additions & 0 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,222 @@
    // configuration
    const colorVariable = 'population'
    const geoIDVariable = 'id'
    const format = d3.format(',')

    // Set tooltips
    const tip = d3
    .tip()
    .attr('class', 'd3-tip')
    .offset([-10, 0])
    .html(
    d =>
    `<strong>Country: </strong><span class='details'>${
    d.properties.name
    }<br></span><strong>Population: </strong><span class='details'>${format(
    d[colorVariable]
    )}</span>`
    )

    tip.direction(function(d) {
    if (d.properties.name === 'Antarctica') return 'n'
    // Americas
    if (d.properties.name === 'Greenland') return 's'
    if (d.properties.name === 'Canada') return 'e'
    if (d.properties.name === 'USA') return 'e'
    if (d.properties.name === 'Mexico') return 'e'
    // Europe
    if (d.properties.name === 'Iceland') return 's'
    if (d.properties.name === 'Norway') return 's'
    if (d.properties.name === 'Sweden') return 's'
    if (d.properties.name === 'Finland') return 's'
    if (d.properties.name === 'Russia') return 'w'
    // Asia
    if (d.properties.name === 'China') return 'w'
    if (d.properties.name === 'Japan') return 's'
    // Oceania
    if (d.properties.name === 'Indonesia') return 'w'
    if (d.properties.name === 'Papua New Guinea') return 'w'
    if (d.properties.name === 'Australia') return 'w'
    if (d.properties.name === 'New Zealand') return 'w'
    // otherwise if not specified
    return 'n'
    })

    tip.offset(function(d) {
    // [top, left]
    if (d.properties.name === 'Antarctica') return [0, 0]
    // Americas
    if (d.properties.name === 'Greenland') return [10, -10]
    if (d.properties.name === 'Canada') return [24, -28]
    if (d.properties.name === 'USA') return [-5, 8]
    if (d.properties.name === 'Mexico') return [12, 10]
    if (d.properties.name === 'Chile') return [0, -15]
    // Europe
    if (d.properties.name === 'Iceland') return [15, 0]
    if (d.properties.name === 'Norway') return [10, -28]
    if (d.properties.name === 'Sweden') return [10, -8]
    if (d.properties.name === 'Finland') return [10, 0]
    if (d.properties.name === 'France') return [-9, 66]
    if (d.properties.name === 'Italy') return [-8, -6]
    if (d.properties.name === 'Russia') return [5, 385]
    // Africa
    if (d.properties.name === 'Madagascar') return [-10, 10]
    // Asia
    if (d.properties.name === 'China') return [-16, -8]
    if (d.properties.name === 'Mongolia') return [-5, 0]
    if (d.properties.name === 'Pakistan') return [-10, 13]
    if (d.properties.name === 'India') return [-11, -18]
    if (d.properties.name === 'Nepal') return [-8, 1]
    if (d.properties.name === 'Myanmar') return [-12, 0]
    if (d.properties.name === 'Laos') return [-12, -8]
    if (d.properties.name === 'Vietnam') return [-12, -4]
    if (d.properties.name === 'Japan') return [5, 5]
    // Oceania
    if (d.properties.name === 'Indonesia') return [0, -5]
    if (d.properties.name === 'Papua New Guinea') return [-5, -10]
    if (d.properties.name === 'Australia') return [-15, 0]
    if (d.properties.name === 'New Zealand') return [-15, 0]
    // otherwise if not specified
    return [-10, 0]
    })

    d3.select('body').style('overflow', 'hidden')

    const parentWidth = d3
    .select('body')
    .node()
    .getBoundingClientRect().width
    const margin = { top: 0, right: 0, bottom: 0, left: 0 }
    const width = 960 - margin.left - margin.right
    const height = 500 - margin.top - margin.bottom

    const color = d3
    .scaleQuantile()
    .range([
    'rgb(247,251,255)',
    'rgb(222,235,247)',
    'rgb(198,219,239)',
    'rgb(158,202,225)',
    'rgb(107,174,214)',
    'rgb(66,146,198)',
    'rgb(33,113,181)',
    'rgb(8,81,156)',
    'rgb(8,48,107)',
    'rgb(3,19,43)'
    ])

    const svg = d3
    .select('body')
    .append('svg')
    .attr('width', width)
    .attr('height', height)
    .append('g')
    .attr('class', 'map')

    const projection = d3
    .geoRobinson()
    .scale(148)
    .rotate([352, 0, 0])
    .translate([width / 2, height / 2])

    const path = d3.geoPath().projection(projection)

    svg.call(tip)

    queue()
    .defer(d3.json, 'world_countries.json')
    .defer(d3.tsv, 'world_population.tsv')
    .await(ready)

    function ready(error, geography, data) {
    data.forEach(d => {
    d[colorVariable] = Number(d[colorVariable].replace(',', ''))
    })

    const colorVariableValueByID = {}

    data.forEach(d => {
    colorVariableValueByID[d[geoIDVariable]] = d[colorVariable]
    })
    geography.features.forEach(d => {
    d[colorVariable] = colorVariableValueByID[d.id]
    })

    // calculate ckmeans clusters
    // then use the max value of each cluster
    // as a break
    const numberOfClasses = color.range().length - 1
    const ckmeansClusters = ss.ckmeans(
    data.map(d => d[colorVariable]),
    numberOfClasses
    )
    const ckmeansBreaks = ckmeansClusters.map(d => d3.min(d))
    console.log('numberOfClasses', numberOfClasses)
    console.log('ckmeansClusters', ckmeansClusters)
    console.log('ckmeansBreaks', ckmeansBreaks)

    // set the domain of the color scale based on our data
    color.domain(ckmeansBreaks)
    //
    // .domain(jenksNaturalBreaks)
    //
    // .domain(d3.extent(data, d => d[colorVariable]));
    //
    // .domain([
    // 10000,
    // 100000,
    // 500000,
    // 1000000,
    // 5000000,
    // 10000000,
    // 50000000,
    // 100000000,
    // 500000000,
    // 1500000000
    // ]);

    svg
    .append('g')
    .attr('class', 'countries')
    .selectAll('path')
    .data(geography.features)
    .enter()
    .append('path')
    .attr('d', path)
    .style('fill', d => {
    if (typeof colorVariableValueByID[d.id] !== 'undefined') {
    return color(colorVariableValueByID[d.id])
    }
    return 'white'
    })
    .style('fill-opacity', 0.8)
    .style('stroke', d => {
    if (d[colorVariable] !== 0) {
    return 'white'
    }
    return 'lightgray'
    })
    .style('stroke-width', 1)
    .style('stroke-opacity', 0.5)
    // tooltips
    .on('mouseover', function(d) {
    tip.show(d)
    d3.select(this)
    .style('fill-opacity', 1)
    .style('stroke-opacity', 1)
    .style('stroke-width', 2)
    })
    .on('mouseout', function(d) {
    tip.hide(d)
    d3.select(this)
    .style('fill-opacity', 0.8)
    .style('stroke-opacity', 0.5)
    .style('stroke-width', 1)
    })

    svg
    .append('path')
    .datum(topojson.mesh(geography.features, (a, b) => a.id !== b.id))
    .attr('class', 'names')
    .attr('d', path)
    }
  2. micahstubbs revised this gist Jun 26, 2019. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -77,9 +77,9 @@
    }
    </style>
    <body>
    <script src="http://d3js.org/d3.v4.min.js"></script>
    <script src="http://d3js.org/queue.v1.min.js"></script>
    <script src="http://d3js.org/topojson.v1.min.js"></script>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://d3js.org/queue.v1.min.js"></script>
    <script src="https://d3js.org/topojson.v1.min.js"></script>
    <script src="https://d3js.org/d3-geo-projection.v1.min.js"></script>
    <script src="d3-tip.js"></script>
    <script src='https://unpkg.com/[email protected]/dist/simple-statistics.min.js'></script>
  3. micahstubbs revised this gist Feb 5, 2017. 1 changed file with 66 additions and 0 deletions.
    66 changes: 66 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -96,6 +96,72 @@
    .offset([-10, 0])
    .html(d => `<strong>Country: </strong><span class='details'>${d.properties.name}<br></span><strong>Population: </strong><span class='details'>${format(d[colorVariable])}</span>`);

    tip.direction(function(d) {
    if (d.properties.name === 'Antarctica') return 'n';
    // Americas
    if (d.properties.name === 'Greenland') return 's';
    if (d.properties.name === 'Canada') return 'e';
    if (d.properties.name === 'USA') return 'e';
    if (d.properties.name === 'Mexico') return 'e';
    // Europe
    if (d.properties.name === 'Iceland') return 's';
    if (d.properties.name === 'Norway') return 's';
    if (d.properties.name === 'Sweden') return 's';
    if (d.properties.name === 'Finland') return 's';
    if (d.properties.name === 'Russia') return 'w';
    // Asia
    if (d.properties.name === 'China') return 'w';
    if (d.properties.name === 'Japan') return 's';
    // Oceania
    if (d.properties.name === 'Indonesia') return 'w';
    if (d.properties.name === 'Papua New Guinea') return 'w';
    if (d.properties.name === 'Australia') return 'w';
    if (d.properties.name === 'New Zealand') return 'w';
    // otherwise if not specified
    return 'n';
    })

    tip.offset(function(d) { // [top, left]
    if (d.properties.name === 'Antarctica') return [0, 0];
    // Americas
    if (d.properties.name === 'Greenland') return [10, -10];
    if (d.properties.name === 'Canada') return [24, -28];
    if (d.properties.name === 'USA') return [-5, 8];
    if (d.properties.name === 'Mexico') return [12, 10];
    if (d.properties.name === 'Chile') return [0, -15];
    // Europe
    if (d.properties.name === 'Iceland') return [15, 0];
    if (d.properties.name === 'Norway') return [10, -28];
    if (d.properties.name === 'Sweden') return [10, -8];
    if (d.properties.name === 'Finland') return [10, 0];
    if (d.properties.name === 'France') return [-9, 66];
    if (d.properties.name === 'Italy') return [-8, -6];
    if (d.properties.name === 'Russia') return [5, 385];
    // Africa
    if (d.properties.name === 'Madagascar') return [-10, 10];
    // Asia
    if (d.properties.name === 'China') return [-16, -8];
    if (d.properties.name === 'Mongolia') return [-5, 0];
    if (d.properties.name === 'Pakistan') return [-10, 13];
    if (d.properties.name === 'India') return [-11, -18];
    if (d.properties.name === 'Nepal') return [-8, 1];
    if (d.properties.name === 'Myanmar') return [-12, 0];
    if (d.properties.name === 'Laos') return [-12, -8];
    if (d.properties.name === 'Vietnam') return [-12, -4];
    if (d.properties.name === 'Japan') return [5, 5];
    // Oceania
    if (d.properties.name === 'Indonesia') return [0, -5];
    if (d.properties.name === 'Papua New Guinea') return [-5, -10];
    if (d.properties.name === 'Australia') return [-15, 0];
    if (d.properties.name === 'New Zealand') return [-15, 0];
    // otherwise if not specified
    return [-10, 0];
    })

    d3.select('body')
    .style('overflow', 'hidden');

    const parentWidth = d3.select('body').node().getBoundingClientRect().width;
    const margin = {top: 0, right: 0, bottom: 0, left: 0};
    const width = 960 - margin.left - margin.right;
    const height = 500 - margin.top - margin.bottom;
  4. micahstubbs revised this gist Sep 22, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@ in this iteration, we use the [ckmeans](http://simplestatistics.org/docs/#ckmean

    this is method is my current favorite way to create breaks, or color thresholds, for a choropleth map.

    🎩 [@Elijah_Meeks](https://twitter.com/Elijah_Meeks) for [the idea](https://d3js.slack.com/archives/help/p1474482980000316) to try the [ckmeans](http://simplestatistics.org/docs/#ckmeans) algorithm, as it's the new hotness in the choropleth map breaks [scene](https://d3js.slack.com/)
    🎩 [@Elijah_Meeks](https://twitter.com/Elijah_Meeks) for [the idea](https://d3js.slack.com/archives/help/p1474482980000316) to try the [ckmeans](http://simplestatistics.org/docs/#ckmeans) algorithm, as it seems to be the new hotness in the choropleth map breaks [scene](https://d3js.slack.com/)

    a further 🙏 to [@recifs](https://twitter.com/recifs) for [talking through](https://d3js.slack.com/archives/help/p1474484449000325) where in the [ckmeans](http://simplestatistics.org/docs/#ckmeans) clusters its reasonable to pick breaks from.
    tl;dr `any number between max(class n) and min(class n+1) is OK`
  5. micahstubbs revised this gist Sep 22, 2016. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,3 @@
    # 09 ckmeans cluster min breaks

    in this iteration, we use the [ckmeans](http://simplestatistics.org/docs/#ckmeans) algorithm from the [simple-statistics](http://simplestatistics.org/) package to cluster our data. we pick the **minimum** value of each cluster as a break. we then use these breaks with a [quantile](https://github.com/d3/d3-scale/blob/master/README.md#quantile-scales) scale to map values in the data to colors on the choropleth map.

    this is method is my current favorite way to create breaks, or color thresholds, for a choropleth map.
  6. micahstubbs revised this gist Sep 22, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .block
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,3 @@
    height: 800
    height: 510
    border: no
    license CC0-1.0
  7. micahstubbs revised this gist Sep 22, 2016. 1 changed file with 5 additions and 0 deletions.
    5 changes: 5 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,11 @@ in this iteration, we use the [ckmeans](http://simplestatistics.org/docs/#ckmean

    this is method is my current favorite way to create breaks, or color thresholds, for a choropleth map.

    🎩 [@Elijah_Meeks](https://twitter.com/Elijah_Meeks) for [the idea](https://d3js.slack.com/archives/help/p1474482980000316) to try the [ckmeans](http://simplestatistics.org/docs/#ckmeans) algorithm, as it's the new hotness in the choropleth map breaks [scene](https://d3js.slack.com/)

    a further 🙏 to [@recifs](https://twitter.com/recifs) for [talking through](https://d3js.slack.com/archives/help/p1474484449000325) where in the [ckmeans](http://simplestatistics.org/docs/#ckmeans) clusters its reasonable to pick breaks from.
    tl;dr `any number between max(class n) and min(class n+1) is OK`

    do check out the other examples in this `world map` series:

    [world map 00 original example](https://bl.ocks.org/jeremycflin/b43ab253f3ae02dced07)
  8. micahstubbs revised this gist Sep 22, 2016. 1 changed file with 12 additions and 0 deletions.
    12 changes: 12 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -4,3 +4,15 @@ in this iteration, we use the [ckmeans](http://simplestatistics.org/docs/#ckmean

    this is method is my current favorite way to create breaks, or color thresholds, for a choropleth map.

    do check out the other examples in this `world map` series:

    [world map 00 original example](https://bl.ocks.org/jeremycflin/b43ab253f3ae02dced07)
    [world map 01 fix tooltip value](https://bl.ocks.org/micahstubbs/01529b106c93f9b649c4006de5c79b80)
    [world map 02 d3 v4](https://bl.ocks.org/micahstubbs/8e15870eb432a21f0bc4d3d527b2d14f)
    [world map 03 es2015 + update code style](https://bl.ocks.org/micahstubbs/281d7b7a7e39a9b59cf80f1b8bd41a72)
    [world map 04 manual breaks + threshold scale](https://bl.ocks.org/micahstubbs/535e57a3a2954a129c13701fe61c681d)
    [world map 05 linear breaks + quantize scale](https://bl.ocks.org/micahstubbs/c14d8bda8e337da6c836a526ad1a7c5a)
    [world map 06 linear breaks + quantiles scale](https://bl.ocks.org/micahstubbs/536bc140537c1f90bf01f0bb9adc87b8)
    [world map 07 Jenks natural breaks](https://bl.ocks.org/micahstubbs/8fc2a6477f5d731dc97887a958f6826d)
    [world map 08 ckmeans cluster max breaks](https://bl.ocks.org/micahstubbs/9c2397c1da11c7b5d331653bcd475c1f)
    **world map 09 ckmeans cluster min breaks**
  9. micahstubbs revised this gist Sep 22, 2016. 1 changed file with 0 additions and 14 deletions.
    14 changes: 0 additions & 14 deletions .eslintrc
    Original file line number Diff line number Diff line change
    @@ -1,14 +0,0 @@
    {
    "ecmaFeatures": {
    "modules": true
    },
    "env": {
    "browser": true,
    "node": true
    },
    "parser": "babel-eslint",
    "rules": {
    "quotes": [2, "single"],
    "strict": [2, "never"],
    }
    }
  10. micahstubbs created this gist Sep 22, 2016.
    3 changes: 3 additions & 0 deletions .block
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    height: 800
    border: no
    license CC0-1.0
    14 changes: 14 additions & 0 deletions .eslintrc
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,14 @@
    {
    "ecmaFeatures": {
    "modules": true
    },
    "env": {
    "browser": true,
    "node": true
    },
    "parser": "babel-eslint",
    "rules": {
    "quotes": [2, "single"],
    "strict": [2, "never"],
    }
    }
    6 changes: 6 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    # 09 ckmeans cluster min breaks

    in this iteration, we use the [ckmeans](http://simplestatistics.org/docs/#ckmeans) algorithm from the [simple-statistics](http://simplestatistics.org/) package to cluster our data. we pick the **minimum** value of each cluster as a break. we then use these breaks with a [quantile](https://github.com/d3/d3-scale/blob/master/README.md#quantile-scales) scale to map values in the data to colors on the choropleth map.

    this is method is my current favorite way to create breaks, or color thresholds, for a choropleth map.

    339 changes: 339 additions & 0 deletions d3-tip.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,339 @@
    /**
    * d3.tip
    * Copyright (c) 2013 Justin Palmer
    *
    * Tooltips for d3.js SVG visualizations
    */
    // eslint-disable-next-line no-extra-semi
    ;(function(root, factory) {
    if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module with d3 as a dependency.
    define([
    'd3-collection',
    'd3-selection'
    ], factory)
    } else if (typeof module === 'object' && module.exports) {
    /* eslint-disable global-require */
    // CommonJS
    var d3Collection = require('d3-collection'),
    d3Selection = require('d3-selection')
    module.exports = factory(d3Collection, d3Selection)
    /* eslint-enable global-require */
    } else {
    // Browser global.
    var d3 = root.d3
    // eslint-disable-next-line no-param-reassign
    root.d3.tip = factory(d3, d3)
    }
    }(this, function(d3Collection, d3Selection) {
    // Public - contructs a new tooltip
    //
    // Returns a tip
    return function() {
    var direction = d3TipDirection,
    offset = d3TipOffset,
    html = d3TipHTML,
    node = initNode(),
    svg = null,
    point = null,
    target = null

    function tip(vis) {
    svg = getSVGNode(vis)
    if (!svg) return
    point = svg.createSVGPoint()
    document.body.appendChild(node)
    }

    // Public - show the tooltip on the screen
    //
    // Returns a tip
    tip.show = function() {
    var args = Array.prototype.slice.call(arguments)
    if (args[args.length - 1] instanceof SVGElement) target = args.pop()

    var content = html.apply(this, args),
    poffset = offset.apply(this, args),
    dir = direction.apply(this, args),
    nodel = getNodeEl(),
    i = directions.length,
    coords,
    scrollTop = document.documentElement.scrollTop ||
    document.body.scrollTop,
    scrollLeft = document.documentElement.scrollLeft ||
    document.body.scrollLeft

    nodel.html(content)
    .style('opacity', 1).style('pointer-events', 'all')

    while (i--) nodel.classed(directions[i], false)
    coords = directionCallbacks.get(dir).apply(this)
    nodel.classed(dir, true)
    .style('top', (coords.top + poffset[0]) + scrollTop + 'px')
    .style('left', (coords.left + poffset[1]) + scrollLeft + 'px')

    return tip
    }

    // Public - hide the tooltip
    //
    // Returns a tip
    tip.hide = function() {
    var nodel = getNodeEl()
    nodel.style('opacity', 0).style('pointer-events', 'none')
    return tip
    }

    // Public: Proxy attr calls to the d3 tip container.
    // Sets or gets attribute value.
    //
    // n - name of the attribute
    // v - value of the attribute
    //
    // Returns tip or attribute value
    // eslint-disable-next-line no-unused-vars
    tip.attr = function(n, v) {
    if (arguments.length < 2 && typeof n === 'string') {
    return getNodeEl().attr(n)
    }

    var args = Array.prototype.slice.call(arguments)
    d3Selection.selection.prototype.attr.apply(getNodeEl(), args)
    return tip
    }

    // Public: Proxy style calls to the d3 tip container.
    // Sets or gets a style value.
    //
    // n - name of the property
    // v - value of the property
    //
    // Returns tip or style property value
    // eslint-disable-next-line no-unused-vars
    tip.style = function(n, v) {
    if (arguments.length < 2 && typeof n === 'string') {
    return getNodeEl().style(n)
    }

    var args = Array.prototype.slice.call(arguments)
    d3Selection.selection.prototype.style.apply(getNodeEl(), args)
    return tip
    }

    // Public: Set or get the direction of the tooltip
    //
    // v - One of n(north), s(south), e(east), or w(west), nw(northwest),
    // sw(southwest), ne(northeast) or se(southeast)
    //
    // Returns tip or direction
    tip.direction = function(v) {
    if (!arguments.length) return direction
    direction = v == null ? v : functor(v)

    return tip
    }

    // Public: Sets or gets the offset of the tip
    //
    // v - Array of [x, y] offset
    //
    // Returns offset or
    tip.offset = function(v) {
    if (!arguments.length) return offset
    offset = v == null ? v : functor(v)

    return tip
    }

    // Public: sets or gets the html value of the tooltip
    //
    // v - String value of the tip
    //
    // Returns html value or tip
    tip.html = function(v) {
    if (!arguments.length) return html
    html = v == null ? v : functor(v)

    return tip
    }

    // Public: destroys the tooltip and removes it from the DOM
    //
    // Returns a tip
    tip.destroy = function() {
    if (node) {
    getNodeEl().remove()
    node = null
    }
    return tip
    }

    function d3TipDirection() { return 'n' }
    function d3TipOffset() { return [0, 0] }
    function d3TipHTML() { return ' ' }

    var directionCallbacks = d3Collection.map({
    n: directionNorth,
    s: directionSouth,
    e: directionEast,
    w: directionWest,
    nw: directionNorthWest,
    ne: directionNorthEast,
    sw: directionSouthWest,
    se: directionSouthEast
    }),
    directions = directionCallbacks.keys()

    function directionNorth() {
    var bbox = getScreenBBox()
    return {
    top: bbox.n.y - node.offsetHeight,
    left: bbox.n.x - node.offsetWidth / 2
    }
    }

    function directionSouth() {
    var bbox = getScreenBBox()
    return {
    top: bbox.s.y,
    left: bbox.s.x - node.offsetWidth / 2
    }
    }

    function directionEast() {
    var bbox = getScreenBBox()
    return {
    top: bbox.e.y - node.offsetHeight / 2,
    left: bbox.e.x
    }
    }

    function directionWest() {
    var bbox = getScreenBBox()
    return {
    top: bbox.w.y - node.offsetHeight / 2,
    left: bbox.w.x - node.offsetWidth
    }
    }

    function directionNorthWest() {
    var bbox = getScreenBBox()
    return {
    top: bbox.nw.y - node.offsetHeight,
    left: bbox.nw.x - node.offsetWidth
    }
    }

    function directionNorthEast() {
    var bbox = getScreenBBox()
    return {
    top: bbox.ne.y - node.offsetHeight,
    left: bbox.ne.x
    }
    }

    function directionSouthWest() {
    var bbox = getScreenBBox()
    return {
    top: bbox.sw.y,
    left: bbox.sw.x - node.offsetWidth
    }
    }

    function directionSouthEast() {
    var bbox = getScreenBBox()
    return {
    top: bbox.se.y,
    left: bbox.se.x
    }
    }

    function initNode() {
    var div = d3Selection.select(document.createElement('div'))
    div
    .style('position', 'absolute')
    .style('top', 0)
    .style('opacity', 0)
    .style('pointer-events', 'none')
    .style('box-sizing', 'border-box')

    return div.node()
    }

    function getSVGNode(element) {
    var svgNode = element.node()
    if (!svgNode) return null
    if (svgNode.tagName.toLowerCase() === 'svg') return svgNode
    return svgNode.ownerSVGElement
    }

    function getNodeEl() {
    if (node == null) {
    node = initNode()
    // re-add node to DOM
    document.body.appendChild(node)
    }
    return d3Selection.select(node)
    }

    // Private - gets the screen coordinates of a shape
    //
    // Given a shape on the screen, will return an SVGPoint for the directions
    // n(north), s(south), e(east), w(west), ne(northeast), se(southeast),
    // nw(northwest), sw(southwest).
    //
    // +-+-+
    // | |
    // + +
    // | |
    // +-+-+
    //
    // Returns an Object {n, s, e, w, nw, sw, ne, se}
    function getScreenBBox() {
    var targetel = target || d3Selection.event.target

    while (targetel.getScreenCTM == null && targetel.parentNode == null) {
    targetel = targetel.parentNode
    }

    var bbox = {},
    matrix = targetel.getScreenCTM(),
    tbbox = targetel.getBBox(),
    width = tbbox.width,
    height = tbbox.height,
    x = tbbox.x,
    y = tbbox.y

    point.x = x
    point.y = y
    bbox.nw = point.matrixTransform(matrix)
    point.x += width
    bbox.ne = point.matrixTransform(matrix)
    point.y += height
    bbox.se = point.matrixTransform(matrix)
    point.x -= width
    bbox.sw = point.matrixTransform(matrix)
    point.y -= height / 2
    bbox.w = point.matrixTransform(matrix)
    point.x += width
    bbox.e = point.matrixTransform(matrix)
    point.x -= width / 2
    point.y -= height / 2
    bbox.n = point.matrixTransform(matrix)
    point.y += height
    bbox.s = point.matrixTransform(matrix)

    return bbox
    }

    // Private - replace D3JS 3.X d3.functor() function
    function functor(v) {
    return typeof v === 'function' ? v : function() {
    return v
    }
    }

    return tip
    }
    // eslint-disable-next-line semi
    }));
    224 changes: 224 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,224 @@
    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>
    .names {
    fill: none;
    stroke: #fff;
    stroke-linejoin: round;
    }

    /* Tooltip CSS */
    .d3-tip {
    line-height: 1.5;
    font-weight: 400;
    font-family:"avenir next", Arial, sans-serif;
    padding: 6px;
    background: rgba(0, 0, 0, 0.6);
    color: #FFA500;
    border-radius: 1px;
    pointer-events: none;
    }

    /* Creates a small triangle extender for the tooltip */
    .d3-tip:after {
    box-sizing: border-box;
    display: inline;
    font-size: 8px;
    width: 100%;
    line-height: 1.5;
    color: rgba(0, 0, 0, 0.6);
    position: absolute;
    pointer-events: none;

    }

    /* Northward tooltips */
    .d3-tip.n:after {
    content: "\25BC";
    margin: -1px 0 0 0;
    top: 100%;
    left: 0;
    text-align: center;
    }

    /* Eastward tooltips */
    .d3-tip.e:after {
    content: "\25C0";
    margin: -4px 0 0 0;
    top: 50%;
    left: -8px;
    }

    /* Southward tooltips */
    .d3-tip.s:after {
    content: "\25B2";
    margin: 0 0 1px 0;
    top: -8px;
    left: 0;
    text-align: center;
    }

    /* Westward tooltips */
    .d3-tip.w:after {
    content: "\25B6";
    margin: -4px 0 0 -1px;
    top: 50%;
    left: 100%;
    }

    /*
    text{
    pointer-events:none;
    }
    */

    .details{
    color: white;
    }
    </style>
    <body>
    <script src="http://d3js.org/d3.v4.min.js"></script>
    <script src="http://d3js.org/queue.v1.min.js"></script>
    <script src="http://d3js.org/topojson.v1.min.js"></script>
    <script src="https://d3js.org/d3-geo-projection.v1.min.js"></script>
    <script src="d3-tip.js"></script>
    <script src='https://unpkg.com/[email protected]/dist/simple-statistics.min.js'></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.10.3/babel.min.js'></script>
    <script lang='babel' type='text/babel'>
    // configuration
    const colorVariable = 'population';
    const geoIDVariable = 'id';
    const format = d3.format(',');

    // Set tooltips
    const tip = d3.tip()
    .attr('class', 'd3-tip')
    .offset([-10, 0])
    .html(d => `<strong>Country: </strong><span class='details'>${d.properties.name}<br></span><strong>Population: </strong><span class='details'>${format(d[colorVariable])}</span>`);

    const margin = {top: 0, right: 0, bottom: 0, left: 0};
    const width = 960 - margin.left - margin.right;
    const height = 500 - margin.top - margin.bottom;

    const color = d3.scaleQuantile()
    .range([
    'rgb(247,251,255)',
    'rgb(222,235,247)',
    'rgb(198,219,239)',
    'rgb(158,202,225)',
    'rgb(107,174,214)',
    'rgb(66,146,198)',
    'rgb(33,113,181)',
    'rgb(8,81,156)',
    'rgb(8,48,107)',
    'rgb(3,19,43)'
    ]);


    const svg = d3.select('body')
    .append('svg')
    .attr('width', width)
    .attr('height', height)
    .append('g')
    .attr('class', 'map');

    const projection = d3.geoRobinson()
    .scale(148)
    .rotate([352, 0, 0])
    .translate( [width / 2, height / 2]);

    const path = d3.geoPath().projection(projection);

    svg.call(tip);

    queue()
    .defer(d3.json, 'world_countries.json')
    .defer(d3.tsv, 'world_population.tsv')
    .await(ready);

    function ready(error, geography, data) {
    data.forEach(d => {
    d[colorVariable] = Number(d[colorVariable].replace(',', ''));
    })

    const colorVariableValueByID = {};

    data.forEach(d => { colorVariableValueByID[d[geoIDVariable]] = d[colorVariable]; });
    geography.features.forEach(d => { d[colorVariable] = colorVariableValueByID[d.id] });

    // calculate ckmeans clusters
    // then use the max value of each cluster
    // as a break
    const numberOfClasses = color.range().length - 1;
    const ckmeansClusters = ss.ckmeans(data.map(d => d[colorVariable]), numberOfClasses);
    const ckmeansBreaks = ckmeansClusters.map(d => d3.min(d));
    console.log('numberOfClasses', numberOfClasses);
    console.log('ckmeansClusters', ckmeansClusters);
    console.log('ckmeansBreaks', ckmeansBreaks);

    // set the domain of the color scale based on our data
    color
    .domain(ckmeansBreaks);
    //
    // .domain(jenksNaturalBreaks)
    //
    // .domain(d3.extent(data, d => d[colorVariable]));
    //
    // .domain([
    // 10000,
    // 100000,
    // 500000,
    // 1000000,
    // 5000000,
    // 10000000,
    // 50000000,
    // 100000000,
    // 500000000,
    // 1500000000
    // ]);

    svg.append('g')
    .attr('class', 'countries')
    .selectAll('path')
    .data(geography.features)
    .enter().append('path')
    .attr('d', path)
    .style('fill', d => {
    if (typeof colorVariableValueByID[d.id] !== 'undefined') {
    return color(colorVariableValueByID[d.id])
    }
    return 'white'
    })
    .style('fill-opacity',0.8)
    .style('stroke', d => {
    if (d[colorVariable] !== 0) {
    return 'white';
    }
    return 'lightgray';
    })
    .style('stroke-width', 1)
    .style('stroke-opacity', 0.5)
    // tooltips
    .on('mouseover',function(d){
    tip.show(d);
    d3.select(this)
    .style('fill-opacity', 1)
    .style('stroke-opacity', 1)
    .style('stroke-width', 2)
    })
    .on('mouseout', function(d){
    tip.hide(d);
    d3.select(this)
    .style('fill-opacity', 0.8)
    .style('stroke-opacity', 0.5)
    .style('stroke-width', 1)
    });

    svg.append('path')
    .datum(topojson.mesh(geography.features, (a, b) => a.id !== b.id))
    .attr('class', 'names')
    .attr('d', path);
    }
    </script>
    </body>
    </html>
    152 changes: 152 additions & 0 deletions jenks.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,152 @@
    // # [Jenks natural breaks optimization](http://en.wikipedia.org/wiki/Jenks_natural_breaks_optimization)
    //
    // Implementations: [1](http://danieljlewis.org/files/2010/06/Jenks.pdf) (python),
    // [2](https://github.com/vvoovv/djeo-jenks/blob/master/main.js) (buggy),
    // [3](https://github.com/simogeo/geostats/blob/master/lib/geostats.js#L407) (works)
    function jenks(data, n_classes) {

    // Compute the matrices required for Jenks breaks. These matrices
    // can be used for any classing of data with `classes <= n_classes`
    function getMatrices(data, n_classes) {

    // in the original implementation, these matrices are referred to
    // as `LC` and `OP`
    //
    // * lower_class_limits (LC): optimal lower class limits
    // * variance_combinations (OP): optimal variance combinations for all classes
    var lower_class_limits = [],
    variance_combinations = [],
    // loop counters
    i, j,
    // the variance, as computed at each step in the calculation
    variance = 0;

    // Initialize and fill each matrix with zeroes
    for (i = 0; i < data.length + 1; i++) {
    var tmp1 = [], tmp2 = [];
    for (j = 0; j < n_classes + 1; j++) {
    tmp1.push(0);
    tmp2.push(0);
    }
    lower_class_limits.push(tmp1);
    variance_combinations.push(tmp2);
    }

    for (i = 1; i < n_classes + 1; i++) {
    lower_class_limits[1][i] = 1;
    variance_combinations[1][i] = 0;
    // in the original implementation, 9999999 is used but
    // since Javascript has `Infinity`, we use that.
    for (j = 2; j < data.length + 1; j++) {
    variance_combinations[j][i] = Infinity;
    }
    }

    for (var l = 2; l < data.length + 1; l++) {

    // `SZ` originally. this is the sum of the values seen thus
    // far when calculating variance.
    var sum = 0,
    // `ZSQ` originally. the sum of squares of values seen
    // thus far
    sum_squares = 0,
    // `WT` originally. This is the number of
    w = 0,
    // `IV` originally
    i4 = 0;

    // in several instances, you could say `Math.pow(x, 2)`
    // instead of `x * x`, but this is slower in some browsers
    // introduces an unnecessary concept.
    for (var m = 1; m < l + 1; m++) {

    // `III` originally
    var lower_class_limit = l - m + 1,
    val = data[lower_class_limit - 1];

    // here we're estimating variance for each potential classing
    // of the data, for each potential number of classes. `w`
    // is the number of data points considered so far.
    w++;

    // increase the current sum and sum-of-squares
    sum += val;
    sum_squares += val * val;

    // the variance at this point in the sequence is the difference
    // between the sum of squares and the total x 2, over the number
    // of samples.
    variance = sum_squares - (sum * sum) / w;

    i4 = lower_class_limit - 1;

    if (i4 !== 0) {
    for (j = 2; j < n_classes + 1; j++) {
    // if adding this element to an existing class
    // will increase its variance beyond the limit, break
    // the class at this point, setting the lower_class_limit
    // at this point.
    if (variance_combinations[l][j] >=
    (variance + variance_combinations[i4][j - 1])) {
    lower_class_limits[l][j] = lower_class_limit;
    variance_combinations[l][j] = variance +
    variance_combinations[i4][j - 1];
    }
    }
    }
    }

    lower_class_limits[l][1] = 1;
    variance_combinations[l][1] = variance;
    }

    // return the two matrices. for just providing breaks, only
    // `lower_class_limits` is needed, but variances can be useful to
    // evaluage goodness of fit.
    return {
    lower_class_limits: lower_class_limits,
    variance_combinations: variance_combinations
    };
    }



    // the second part of the jenks recipe: take the calculated matrices
    // and derive an array of n breaks.
    function breaks(data, lower_class_limits, n_classes) {

    var k = data.length - 1,
    kclass = [],
    countNum = n_classes;

    // the calculation of classes will never include the upper and
    // lower bounds, so we need to explicitly set them
    kclass[n_classes] = data[data.length - 1];
    kclass[0] = data[0];

    // the lower_class_limits matrix is used as indexes into itself
    // here: the `k` variable is reused in each iteration.
    while (countNum > 1) {
    kclass[countNum - 1] = data[lower_class_limits[k][countNum] - 2];
    k = lower_class_limits[k][countNum] - 1;
    countNum--;
    }

    return kclass;
    }

    if (n_classes > data.length) return null;

    // sort data in numerical order, since this is expected
    // by the matrices function
    data = data.slice().sort(function (a, b) { return a - b; });

    // get our basic matrices
    var matrices = getMatrices(data, n_classes),
    // we only need lower class limits here
    lower_class_limits = matrices.lower_class_limits;

    // extract n_classes out of the computed matrices
    return breaks(data, lower_class_limits, n_classes);

    }
    Binary file added preview.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
    Binary file added preview.xcf
    Binary file not shown.
    Binary file added thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
    178 changes: 178 additions & 0 deletions world_countries.json
    178 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
    243 changes: 243 additions & 0 deletions world_population.tsv
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,243 @@
    id name population
    CHN China "1330141295"
    IND India "1173108018"
    USA United States "310232863"
    IDN Indonesia "242968342"
    BRA Brazil "201103330"
    PAK Pakistan "177276594"
    BGD Bangladesh "158065841"
    NGA Nigeria "152217341"
    RUS Russia "139390205"
    JPN Japan "126804433"
    MEX Mexico "112468855"
    PHL Philippines "99900177"
    VNM Vietnam "89571130"
    ETH Ethiopia "88013491"
    DEU Germany "82282988"
    EGY Egypt "80471869"
    TUR Turkey "77804122"
    COD "Congo, Democratic Republic of the" "70916439"
    IRN Iran "67037517"
    THA Thailand "66404688"
    FRA France "64057792"
    GBR United Kingdom "61284806"
    ITA Italy "58090681"
    MMR Burma "53414374"
    ZAF South Africa "49109107"
    KOR "Korea, South" "48636068"
    UKR Ukraine "45415596"
    COL Colombia "44205293"
    SDN Sudan "41980182"
    TZA Tanzania "41892895"
    ARG Argentina "41343201"
    ESP Spain "40548753"
    KEN Kenya "40046566"
    POL Poland "38463689"
    DZA Algeria "34586184"
    CAN Canada "33759742"
    UGA Uganda "33398682"
    MAR Morocco "31627428"
    PER Peru "29907003"
    IRQ Iraq "29671605"
    SAU Saudi Arabia "29207277"
    AFG Afghanistan "29121286"
    NPL Nepal "28951852"
    UZB Uzbekistan "27865738"
    VEN Venezuela "27223228"
    MYS Malaysia "26160256"
    GHA Ghana "24339838"
    YEM Yemen "23495361"
    TWN Taiwan "23024956"
    PRK "Korea, North" "22757275"
    SYR Syria "22198110"
    ROU Romania "22181287"
    MOZ Mozambique "22061451"
    AUS Australia "21515754"
    LKA Sri Lanka "21513990"
    MDG Madagascar "21281844"
    CIV Cote d'Ivoire "21058798"
    CMR Cameroon "19294149"
    NLD Netherlands "16783092"
    CHL Chile "16746491"
    BFA Burkina Faso "16241811"
    NER Niger "15878271"
    KAZ Kazakhstan "15460484"
    MWI Malawi "15447500"
    ECU Ecuador "14790608"
    KHM Cambodia "14753320"
    SEN Senegal "14086103"
    MLI Mali "13796354"
    GTM Guatemala "13550440"
    AGO Angola "13068161"
    ZMB Zambia "12056923"
    ZWE Zimbabwe "11651858"
    CUB Cuba "11477459"
    RWA Rwanda "11055976"
    GRC Greece "10749943"
    PRT Portugal "10735765"
    TUN Tunisia "10589025"
    TCD Chad "10543464"
    BEL Belgium "10423493"
    GIN Guinea "10324025"
    CZE Czech Republic "10201707"
    SOM Somalia "10112453"
    BOL Bolivia "9947418"
    HUN Hungary "9880059"
    BDI Burundi "9863117"
    DOM Dominican Republic "9794487"
    BLR Belarus "9612632"
    HTI Haiti "9203083"
    SWE Sweden "9074055"
    BEN Benin "9056010"
    AZE Azerbaijan "8303512"
    AUT Austria "8214160"
    HND Honduras "7989415"
    CHE Switzerland "7623438"
    TJK Tajikistan "7487489"
    ISR Israel "7353985"
    SRB Serbia "7344847"
    BGR Bulgaria "7148785"
    HKG Hong Kong "7089705"
    LAO Laos "6993767"
    LBY Libya "6461454"
    JOR Jordan "6407085"
    PRY Paraguay "6375830"
    TGO Togo "6199841"
    PNG Papua New Guinea "6064515"
    SLV El Salvador "6052064"
    NIC Nicaragua "5995928"
    ERI Eritrea "5792984"
    DNK Denmark "5515575"
    KGZ Kyrgyzstan "5508626"
    SVK Slovakia "5470306"
    FIN Finland "5255068"
    SLE Sierra Leone "5245695"
    ARE United Arab Emirates "4975593"
    TKM Turkmenistan "4940916"
    CAF Central African Republic "4844927"
    SGP Singapore "4701069" not listed
    NOR Norway "4676305"
    BIH Bosnia and Herzegovina "4621598"
    GEO Georgia "4600825"
    CRI Costa Rica "4516220"
    HRV Croatia "4486881"
    MDA Moldova "4317483"
    NZL New Zealand "4252277"
    IRL Ireland "4250163"
    COG "Congo, Republic of the" "4125916"
    LBN Lebanon "4125247"
    PRI Puerto Rico "3977663"
    LBR Liberia "3685076"
    ALB Albania "3659616"
    LTU Lithuania "3545319"
    URY Uruguay "3510386"
    PAN Panama "3410676"
    MRT Mauritania "3205060"
    MNG Mongolia "3086918"
    OMN Oman "2967717"
    ARM Armenia "2966802"
    JAM Jamaica "2847232"
    KWT Kuwait "2789132"
    PSE West Bank "2514845"
    LVA Latvia "2217969"
    NAM Namibia "2128471"
    MKD Macedonia "2072086"
    BWA Botswana "2029307"
    SVN Slovenia "2003136"
    LSO Lesotho "1919552"
    GMB "Gambia, The" "1824158"
    KWT Kosovo "1815048"
    149 Gaza Strip "1604238" not listed
    GNB Guinea-Bissau "1565126"
    GAB Gabon "1545255"
    SWZ Swaziland "1354051"
    153 Mauritius "1294104" not listed
    EST Estonia "1291170"
    TTO Trinidad and Tobago "1228691"
    TLS Timor-Leste "1154625"
    CYP Cyprus "1102677"
    FJI Fiji "957780"
    QAT Qatar "840926"
    160 Comoros "773407" not listed
    GUY Guyana "748486"
    DJI Djibouti "740528"
    163 Bahrain "738004" not listed
    BTN Bhutan "699847"
    MNE Montenegro "666730"
    GNQ Equatorial Guinea "650702"
    SLB Solomon Islands "609794"
    168 Macau "567957" not listed
    169 Cape Verde "508659" not listed
    LUX Luxembourg "497538"
    ESH Western Sahara "491519"
    SUR Suriname "486618"
    173 Malta "406771" not listed
    174 Maldives "395650" not listed
    BRN Brunei "395027"
    BLZ Belize "314522"
    BHS "Bahamas, The" "310426"
    ISL Iceland "308910"
    179 French Polynesia "291000" not listed
    180 Barbados "285653" not listed
    181 Mayotte "231139" not listed
    NCL New Caledonia "229993"
    183 Netherlands Antilles "228693" not listed
    VUT Vanuatu "221552"
    185 Samoa "192001" not listed
    186 Sao Tome and Principe "175808" not listed
    187 Saint Lucia "160922" not listed
    188 Tonga "122580" not listed
    189 Virgin Islands "109775" not listed
    190 Grenada "107818" not listed
    191 "Micronesia, Federated States of" "107154" not listed
    192 Aruba "104589" not listed
    193 Saint Vincent and the Grenadines "104217" not listed
    194 Kiribati "99482" not listed
    195 Jersey "91812" not listed
    196 Seychelles "88340" not listed
    197 Antigua and Barbuda "86754" not listed
    198 Andorra "84525" not listed
    199 Isle of Man "76913" not listed
    DOM Dominica "72813"
    201 Bermuda "68268" not listed
    202 American Samoa "66432" not listed
    203 Marshall Islands "65859" not listed
    204 Guernsey "65632" not listed
    GRL Greenland "57637"
    206 Cayman Islands "50209" not listed
    207 Saint Kitts and Nevis "49898" not listed
    208 Faroe Islands "49057" not listed
    209 Northern Mariana Islands "48317" not listed
    210 Liechtenstein "35002" not listed
    211 San Marino "31477" not listed
    212 Monaco "30586" not listed
    213 Saint Martin "30235" not listed
    214 Gibraltar "28877" not listed
    215 British Virgin Islands "24939" not listed
    216 Turks and Caicos Islands "23528" not listed
    217 Palau "20879" not listed
    218 Akrotiri "15700" not listed
    219 Dhekelia "15700" not listed
    220 Wallis and Futuna "15343" not listed
    221 Anguilla "14764" not listed
    222 Nauru "14264" not listed
    223 Cook Islands "11488" not listed
    224 Tuvalu "10472" not listed
    225 "Saint Helena, Ascension, and Tristan da Cunha" "7670" not listed
    226 Saint Barthelemy "7406" not listed
    227 Saint Pierre and Miquelon "6010" not listed
    228 Montserrat "5118" not listed
    FLK Falkland Islands (Islas Malvinas) "3140"
    230 Norfolk Island "2155" not listed
    231 Svalbard "2067" not listed
    232 Christmas Island "1402" not listed
    233 Tokelau "1400" not listed
    234 Niue "1354" not listed
    235 Holy See (Vatican City) 829 not listed
    236 Cocos (Keeling) Islands 596 not listed
    237 Pitcairn Islands 48 not listed
    ATA Antarctica 0
    ATF French Southern and Antarctic Lands 0
    SDS South Sudan "12152321"
    ABV Somaliland "3500000"
    OSA Kosovo "1824000"