block by denisemauldin cb870e6f439864a5ae74d4fc561ac46f

d3v5 world map 04 manual breaks + threshold scale

Full Screen

this iteration uses the Robinson Projection from d3-geo-projection

it also uses a threshold scale with manually specified breaks to map values in the data to colors on the choropleth map

🎩 @jeremy_cf_lin for suggesting the Robinson Projection over in the #help channel of the d3js slack

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

world map 00 original example
world map 01 fix tooltip value
world map 02 d3 v4
world map 03 es2015 + update code style
world map 04 manual breaks + threshold scale
world map 05 linear breaks + quantize scale
world map 06 linear breaks + quantiles scale
world map 07 Jenks natural breaks
world map 08 ckmeans cluster max breaks
world map 09 ckmeans cluster min breaks

forked from micahstubbs‘s block: world map 04 manual breaks + threshold scale

forked from Fil‘s block: world map 04 manual breaks + threshold scale d3v5 [UNLISTED]

index.html

<!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="//d3js.org/d3.v5.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script src="d3-tip.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'>
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.population)}</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.scaleThreshold()
  .domain([
    10000,
    100000,
    500000,
    1000000,
    5000000,
    10000000,
    50000000,
    100000000,
    500000000,
    1500000000
  ])
  .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);

Promise.all([
	d3.json('world_countries.json'),
  d3.tsv ('world_population.tsv')
]).then(
  d => ready(null, d[0], d[1])
);

function ready(error, data, population) {
  const populationById = {};

  population.forEach(d => { populationById[d.id] = +d.population; });
  data.features.forEach(d => { d.population = populationById[d.id] });

  svg.append('g')
    .attr('class', 'countries')
    .selectAll('path')
    .data(data.features)
    .enter().append('path')
      .attr('d', path)
      .style('fill', d => color(populationById[d.id]))
      .style('stroke', 'white')
      .style('opacity', 0.8)
      .style('stroke-width', 0.3)
      // tooltips
      .on('mouseover',function(d){
        tip.show(d);
        d3.select(this)
          .style('opacity', 1)
          .style('stroke-width', 3);
      })
      .on('mouseout', function(d){
        tip.hide(d);
        d3.select(this)
          .style('opacity', 0.8)
          .style('stroke-width',0.3);
      });

  svg.append('path')
    .datum(topojson.mesh(data.features, (a, b) => a.id !== b.id))
    .attr('class', 'names')
    .attr('d', path);
}
</script>
</body>
</html>

d3-tip.js

/**
 * 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
}));

world_population.tsv

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"