-
-
Save kozikow/e22e67d6ecdebbf37ac8c130cb24dc7f to your computer and use it in GitHub Desktop.
TF Coverage map
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
height: 510 | |
border: no | |
license CC0-1.0 |
We can make this file beautiful and searchable if this error is corrected: Any value after quoted field isn't allowed in line 241.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
id name country_readiness | |
CHN China "3" | |
IND India "1" | |
USA United States "5" | |
IDN Indonesia "2" | |
BRA Brazil "3" | |
PAK Pakistan "1" | |
BGD Bangladesh "2" | |
NGA Nigeria "1" | |
RUS Russia "3" | |
JPN Japan "4" | |
MEX Mexico "3" | |
PHL Philippines "2" | |
VNM Vietnam "2" | |
ETH Ethiopia "0" | |
DEU Germany "3" | |
EGY Egypt "2" | |
TUR Turkey "3" | |
COD "Congo, Democratic Republic of the" "0" | |
IRN Iran "1" | |
THA Thailand "2" | |
FRA France "5" | |
GBR United Kingdom "5" | |
ITA Italy "4" | |
MMR Burma "0" | |
ZAF South Africa "3" | |
KOR "Korea, South" "3" | |
UKR Ukraine "0" | |
COL Colombia "2" | |
SDN Sudan "0" | |
TZA Tanzania "0" | |
ARG Argentina "3" | |
ESP Spain "4" | |
KEN Kenya "0" | |
POL Poland "5" | |
DZA Algeria "0" | |
CAN Canada "5" | |
UGA Uganda "0" | |
MAR Morocco "0" | |
PER Peru "2" | |
IRQ Iraq "1" | |
SAU Saudi Arabia "1" | |
AFG Afghanistan "1" | |
NPL Nepal "0" | |
UZB Uzbekistan "0" | |
VEN Venezuela "0" | |
MYS Malaysia "2" | |
GHA Ghana "0" | |
YEM Yemen "0" | |
TWN Taiwan "3" | |
PRK "Korea, North" "0" | |
SYR Syria "0" | |
ROU Romania "4" | |
MOZ Mozambique "0" | |
AUS Australia "5" | |
LKA Sri Lanka "0" | |
MDG Madagascar "0" | |
CIV Cote d'Ivoire "0" | |
CMR Cameroon "0" | |
NLD Netherlands "4" | |
CHL Chile "3" | |
BFA Burkina Faso "0" | |
NER Niger "1" | |
KAZ Kazakhstan "0" | |
MWI Malawi "0" | |
ECU Ecuador "0" | |
KHM Cambodia "0" | |
SEN Senegal "0" | |
MLI Mali "0" | |
GTM Guatemala "0" | |
AGO Angola "0" | |
ZMB Zambia "0" | |
ZWE Zimbabwe "0" | |
CUB Cuba "0" | |
RWA Rwanda "0" | |
GRC Greece "4" | |
PRT Portugal "4" | |
TUN Tunisia "0" | |
TCD Chad "0" | |
BEL Belgium "4" | |
GIN Guinea "0" | |
CZE Czech Republic "4" | |
SOM Somalia "0" | |
BOL Bolivia "0" | |
HUN Hungary "0" | |
BDI Burundi "0" | |
DOM Dominican Republic "0" | |
BLR Belarus "0" | |
HTI Haiti "0" | |
SWE Sweden "4" | |
BEN Benin "0" | |
AZE Azerbaijan "0" | |
AUT Austria "3" | |
HND Honduras "0" | |
CHE Switzerland "4" | |
TJK Tajikistan "0" | |
ISR Israel "2" | |
SRB Serbia "0" | |
BGR Bulgaria "0" | |
HKG Hong Kong "2" | |
LAO Laos "0" | |
LBY Libya "0" | |
JOR Jordan "0" | |
PRY Paraguay "0" | |
TGO Togo "0" | |
PNG Papua New Guinea "0" | |
SLV El Salvador "0" | |
NIC Nicaragua "0" | |
ERI Eritrea "0" | |
DNK Denmark "4" | |
KGZ Kyrgyzstan "0" | |
SVK Slovakia "0" | |
FIN Finland "4" | |
SLE Sierra Leone "0" | |
ARE United Arab Emirates "2" | |
TKM Turkmenistan "0" | |
CAF Central African Republic "0" | |
SGP Singapore "3" | |
NOR Norway "4" | |
BIH Bosnia and Herzegovina "0" | |
GEO Georgia "0" | |
CRI Costa Rica "0" | |
HRV Croatia "0" | |
MDA Moldova "0" | |
NZL New Zealand "4" | |
IRL Ireland "4" | |
COG "Congo, Republic of the" "0" | |
LBN Lebanon "0" | |
PRI Puerto Rico "0" | |
LBR Liberia "0" | |
ALB Albania "0" | |
LTU Lithuania "0" | |
URY Uruguay "0" | |
PAN Panama "0" | |
MRT Mauritania "0" | |
MNG Mongolia "0" | |
OMN Oman "0" | |
ARM Armenia "0" | |
JAM Jamaica "0" | |
KWT Kuwait "0" | |
PSE West Bank "0" | |
LVA Latvia "0" | |
NAM Namibia "0" | |
MKD Macedonia "0" | |
BWA Botswana "0" | |
SVN Slovenia "0" | |
LSO Lesotho "0" | |
GMB "Gambia, The" "0" | |
KWT Kosovo "0" | |
149 Gaza Strip "0" | |
GNB Guinea-Bissau "0" | |
GAB Gabon "0" | |
SWZ Swaziland "0" | |
153 Mauritius "0" | |
EST Estonia "0" | |
TTO Trinidad and Tobago "0" | |
TLS Timor-Leste "0" | |
CYP Cyprus "0" | |
FJI Fiji "0" | |
QAT Qatar "0" | |
160 Comoros "0" | |
GUY Guyana "0" | |
DJI Djibouti "0" | |
163 Bahrain "0" | |
BTN Bhutan "0" | |
MNE Montenegro "0" | |
GNQ Equatorial Guinea "0" | |
SLB Solomon Islands "0" | |
168 Macau "0" | |
169 Cape Verde "0" | |
LUX Luxembourg "0" | |
ESH Western Sahara "0" | |
SUR Suriname "0" | |
173 Malta "0" | |
174 Maldives "0" | |
BRN Brunei "0" | |
BLZ Belize "0" | |
BHS "Bahamas, The" "0" | |
ISL Iceland "0" | |
179 French Polynesia "0" | |
180 Barbados "0" | |
181 Mayotte "0" not listed | |
NCL New Caledonia "0" | |
183 Netherlands Antilles "0" | |
VUT Vanuatu "0" | |
185 Samoa "0" not listed | |
186 Sao Tome and Principe "0" | |
187 Saint Lucia "0" not listed | |
188 Tonga "0" not listed | |
189 Virgin Islands "0" not listed | |
190 Grenada "0" not listed | |
191 "Micronesia, Federated States of" "0" | |
192 Aruba "0" not listed | |
193 Saint Vincent and the Grenadines "0" | |
194 Kiribati "0" | |
195 Jersey "0" | |
196 Seychelles "0" | |
197 Antigua and Barbuda "0" | |
198 Andorra "0" | |
199 Isle of Man "0" | |
DOM Dominica "0" | |
201 Bermuda "0" not listed | |
202 American Samoa "0" | |
203 Marshall Islands "0" | |
204 Guernsey "0" | |
GRL Greenland "0" | |
206 Cayman Islands "0" | |
207 Saint Kitts and Nevis "0" | |
208 Faroe Islands "0" not listed | |
209 Northern Mariana Islands "0" | |
210 Liechtenstein "0" | |
211 San Marino "0" | |
212 Monaco "0" | |
213 Saint Martin "0" | |
214 Gibraltar "0" | |
215 British Virgin Islands "0" | |
216 Turks and Caicos Islands "0" | |
217 Palau "0" | |
218 Akrotiri "0" | |
219 Dhekelia "0" | |
220 Wallis and Futuna "0" | |
221 Anguilla "0" | |
222 Nauru "0" | |
223 Cook Islands "0" | |
224 Tuvalu "0" | |
225 "Saint Helena, Ascension, and Tristan da Cunha" "0" | |
226 Saint Barthelemy "0" | |
227 Saint Pierre and Miquelon "0" | |
228 Montserrat "0" | |
FLK Falkland Islands (Islas Malvinas) "0" | |
230 Norfolk Island "0" | |
231 Svalbard "0" | |
232 Christmas Island "0" | |
233 Tokelau "0" | |
234 Niue "0" | |
235 Holy See (Vatican City) 0 | |
236 Cocos (Keeling) Islands 0 | |
237 Pitcairn Islands 0 | |
ATA Antarctica 0 | |
ATF French Southern and Antarctic Lands 0 | |
SDS South Sudan "0" | |
ABV Somaliland "0" | |
OSA Kosovo "0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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 | |
})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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="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> | |
<script src='https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.10.3/babel.min.js'></script> | |
<script src='./index.js' lang='babel' type='text/babel'> | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// configuration | |
const colorVariable = 'country_readiness' | |
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>Country Readiness: </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, 'country_readiness.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) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// # [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); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment