One of a set of hierarchical charts I have written for the dc.js library.
Data from https://unstats.un.org/
xxxxxxxxxx
<html lang="en">
<head>
<title>dc.js - Partition Cluster Chart Example</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.2/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/dc/3.0.6/dc.min.css"/>
<style>
.oversize { display: none }
.highlight .oversize, .selected .oversize { display: block }
svg text { font-size: 10px; stroke: none }
.chart-div {
margin-right: 20px;
}
.chart-div.pie {
width: 280px;
}
.page {
margin: 0 40px;
}
#dev {
clear: left;
}
.partition-element rect {
stroke: white;
stroke-width: .5px
}
.dc-chart .selected rect {
stroke-width: 3;
stroke: #ccc;
fill-opacity: 1; }
.dc-chart .deselected rect {
stroke: none;
fill-opacity: .5;
fill: #ccc;
}
.dc-chart .partition-element :hover,
.dc-chart .partition-element.highlight,
.dc-chart .cluster-element :hover,
.dc-chart .cluster-element.highlight
{
fill-opacity: .8;
}
</style>
</head>
<body>
<div class="page">
<h2>dc.js Partition Cluster Chart</h2>
<div id="test"></div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter2/1.4.6/crossfilter.min.js"></script>
<script type="text/javascript" src="d3-shape.js"></script>
<script type="text/javascript" src="dc.min.js"></script>
<script type="text/javascript">
function add_chart ( id, title, ctype, div_class ) {
if ( ! ctype ) ctype = id
if ( ! title ) title = id
if ( ! div_class ) div_class = ''
d3.select('#test')
.append('div')
.classed('chart-div ' + div_class, true)
.attr('id', id)
.append('h4')
.text( title );
return dc[ctype + 'Chart']('#' + id );
}
var tr = {
global: 'Global Name',
region: 'Region Name',
subregion: 'Sub-region Name',
intermed_region: 'Intermediate Region Name',
country: 'Country or Area',
ldc: 'Least Developed Countries (LDC)',
lldc: 'Land Locked Developing Countries (LLDC)',
sids: 'Small Island Developing States (SIDS)',
dev: 'Developed / Developing Countries'
},
popstats_tr = {
year: 'Year',
country: 'Country / territory of asylum/residence',
location: 'Location Name',
female_0_4: 'Female 0-4',
female_5_11: 'Female 5-11',
female_5_17: 'Female 5-17',
female_12_17: 'Female 12-17',
female_18_59: 'Female 18-59',
female_60: 'Female 60+',
f_unknown: 'F: Unknown',
f_total: 'F: Total',
male_0_4: 'Male 0-4',
male_5_11: 'Male 5-11',
male_5_17: 'Male 5-17',
male_12_17: 'Male 12-17',
male_18_59: 'Male 18-59',
male_60: 'Male 60+',
m_unknown: 'M: Unknown',
m_total: 'M: Total',
ppl_total: 'Total number of people'
}
Promise.all([
d3.csv('countries-regions.csv', function (d) {
return {
global: d['Global Name'],
region: d['Region Name'],
subregion: d['Sub-region Name'],
intermed_region: d['Intermediate Region Name'],
country: d['Country or Area'],
};
}),
d3.csv('unhcr_popstats_export_demographics_all_data.csv', function (d) {
return {
year: +d['Year'],
country: d['Country / territory of asylum/residence'],
location: d['Location Name'],
// female_0_4: +d['Female 0-4'],
// female_5_11: +d['Female 5-11'],
// female_5_17: +d['Female 5-17'],
// female_12_17: +d['Female 12-17'],
// female_18_59: +d['Female 18-59'],
// female_60: +d['Female 60+'],
// f_unknown: +d['F: Unknown'],
f_total: +d['F: Total'],
// male_0_4: +d['Male 0-4'],
// male_5_11: +d['Male 5-11'],
// male_5_17: +d['Male 5-17'],
// male_12_17: +d['Male 12-17'],
// male_18_59: +d['Male 18-59'],
// male_60: +d['Male 60+'],
// m_unknown: +d['M: Unknown'],
m_total: +d['M: Total']
};
})
])
.then( function(data) {
var loc_ix = {}
data[0].forEach( c => loc_ix[c.country] = c );
var alt_names = {
'Serbia and Kosovo (S/RES/1244 (1999))': 'Serbia',
'Swaziland': 'Eswatini',
'Bonaire': "Bonaire, Sint Eustatius and Saba",
'United Kingdom': 'United Kingdom of Great Britain and Northern Ireland'
}
var get_country = function (c) {
if ( loc_ix[c] ) {
return c;
}
else if ( alt_names[c] ) {
return alt_names[c];
}
}
var ndx = crossfilter( data[1].filter( d => d.year !== 2017 ) ),
xf_dim = {
country: ndx.dimension( d => {
var c = get_country(d.country)
d.global = loc_ix[c].global ? loc_ix[c].global : 'World'
d.region = loc_ix[ c ].region ? loc_ix[ c ].region : 'unspecified'
d.subregion = loc_ix[c].subregion ? loc_ix[c].subregion : 'unspecified'
d.ppl_total = d.f_total + d.m_total
return [ d.region, d.subregion, d.country ]
}),
m_total: ndx.dimension( d => d.m_total ),
f_total: ndx.dimension( d => d.f_total ),
ppl_total: ndx.dimension( d => d.ppl_total ),
year: ndx.dimension( d => d.year ),
region: ndx.dimension( d => d.region )
}
var chart_ix = {},
cntrls = {}
chart_ix.partition = add_chart('partition', 'Persons of concern by location', 'partitionCluster');
chart_ix.region = add_chart( 'region', 'Persons of concern by region', 'row' );
chart_ix.year = add_chart( 'year', 'Persons of concern by year', 'row' );
// set up colour scales
var num = xf_dim.region.group().top(Infinity).length
var colDomain = xf_dim.region.group().top(Infinity).map( x => x.key.length === 0 ? 'none' : x.key )
.concat(
xf_dim.region.group().top(Infinity).map( x => '_' + x.key ),
xf_dim.region.group().top(Infinity).map( x => '__' + x.key ),
xf_dim.region.group().top(Infinity).map( x => '___' + x.key ),
[ 'World', '_undefined', '-', 'none' ] )
var colRange = d3.schemeSpectral[num].concat(
d3.schemeSpectral[num].map( x => d3.color(x).darker(0.1).hex() ),
d3.schemeSpectral[num].map( x => d3.color(x).darker(0.2).hex() ),
d3.schemeSpectral[num].map( x => d3.color(x).darker(0.3).hex() ),
[ '#999', 'none', 'none', 'none' ]
)
var specscale = d3.scaleOrdinal().domain( colDomain ).range( colRange )
var colorAcc = function (d) {
var key = d.key[0];
if ( d.key.length === 0 ) {
key = 'World';
}
else if ( d.key.length > 1 ) {
key = '_'.repeat(d.key.length-1) + key
}
else if ( key === '' ) {
key = 'none'
}
return key
}
chart_ix['partition']
.width(1000)
.height(1600)
.dimension(xf_dim.country)
.group( xf_dim.country.group().reduceSum( d => d.ppl_total ) )
.colors( specscale )
.colorAccessor( colorAcc )
.horizontalOrientation(true)
.transitionDuration(3000)
.text.rootName('World')
chart_ix.region
.width(500)
.height(300)
.colorAccessor( function(d){ return d.key.length === 0 ? 'none' : d.key })
.colors( specscale )
.dimension(xf_dim.region)
.group(xf_dim.region.group())
.elasticX(true)
var year_cols = d3.scaleOrdinal()
.domain( ndx.dimension( d => d.year ).group().top(Infinity).map( d => d.key ).sort( (a,b) => a-b ) )
.range( d3.schemeGnBu[7] )
chart_ix.year
.width(500)
.height(400)
.dimension(xf_dim.year)
.group(xf_dim.year.group().reduceSum( d => d.ppl_total ) )
.ordering(dc.pluck('year'))
.colorAccessor(dc.pluck('key'))
.colors(year_cols)
.margins({ top: 30, left: 10, bottom: 100, right: 10 })
.elasticX(true)
cntrls.line_type = d3.select('#partition').append('div')
.append('select');
cntrls.line_type
.selectAll('option')
.data(['curved','direct','dogleg','dogleg_alt','l_shape','l_shape_alt'])
.enter()
.append('option')
.attr('value', function(d){ return d })
.text( function(d){ return d })
dc.renderAll();
d3.select('#year g.axis')
.attr('text-anchor', 'start')
d3.selectAll( '#year .axis .tick text')
.attr('transform', 'rotate(45)translate(10)')
cntrls.line_type.on('change',function(e) {
chart_ix.partition.diagonal(this.value).redraw()
});
});
</script>
</div>
</body>
</html>
https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js
https://cdnjs.cloudflare.com/ajax/libs/crossfilter2/1.4.6/crossfilter.min.js