tomgp
3/3/2016 - 7:05 PM

Modal transport share

Modal transport share

Icon array with emojis

A simple example of using the layout and scale functions of my d3 icon array plugin

Country	City	walking	cycling	public transport	private motor vehicle	year
Australia	Adelaide	3	1	10	86	2006
New Zealand	Auckland	3	1	6	89	2009-2012
Spain	Barcelona	35	12	33	20	2012
China	Beijing	21	32	26	21	2005/2011
Germany	Berlin	29	15	26	30	2012
Colombia	Bogota	15	2	64	19	2008
United States	Boston	14	2	35	45	2009
Australia	Brisbane	4	1	14	81	2006
Belgium	Brussels	25	2.5	28	43	2010
Hungary	Budapest	22	2	30	46	2004
United States	Chicago	6	1	27	61	2009
South Korea	Daejeon	26	2	28	44	2012
United States	Dallas	2	0	4	89	2009
India	Delhi	21	12	48	19	2008/2011
Germany	Hamburg	28	12	18	42	2008
United States	Houston	2	0	4	88	2009
United States	Indianapolis	2	1	2	92	2009
United States	Las Vegas	3	0	3	89	2009
United Kingdom	London	21	2	44	34	2011
United States	Los Angeles	3	1	11	78	2009
Spain	Madrid	36	0	34	30	2006
Australia	Melbourne	4	2	14	80	2012
India	Mumbai	27	6	52	15	2008/2011
Germany	Munich	28	17	21	37	2011
United States	New York City	10	1	55	29	2009
Japan	Osaka	27	0	34	39	2000
France	Paris	61	3	27	9	2010
Australia	Perth	3	1	10	86	2006
United States	Philadelphia	9	2	25	60	2009
United States	Phoenix	2	1	3	88	2009
United States	Portland	6	6	12	70	2009
Czech Republic	Prague	23	1	43	33	2009
Italy	Rome	7	0	24	68	2001
United States	San Antonio	2	0	3	90	2009
United States	San Diego	3	1	4	85	2009
United States	San Francisco	10	3	32	46	2009
United States	San Jose	2	1	3	89	2009
United States	Seattle	8	3	20	63	2009
China	Shanghai	27	20	33	20	2009/2011
Singapore	Singapore	22	1	44	33	2011
Australia	Sydney	5	1	21	74	2006
Taiwan	Taipei	15	4	33	48	2009/2010
Japan	Tokyo	23	14	51	12	2008/2009
Canada	Toronto	7	2	34	56	2006
Austria	Vienna	26	7	39	28	2014
Poland	Warsaw	5	1	60	34	2009
United States	"Washington, D.C."	11	2	37	43	2009
<!DOCTYPE html>
<html>
<head>
	<title>Transport modal share</title>
	<script src="//d3js.org/d3.v4.0.0-alpha.18.min.js" charset="utf-8"></script>
	<script type="text/javascript" src="d3-iconarray.js"></script>
	<style type="text/css">
	*{
		font-family: sans-serif;
	}
	button{
		cursor: pointer;
	}
	svg{display:block;}
	.zero{display: none;}
	text{
		font-size: 20px;
	}
	hr{
		border:none;
		border-bottom: 1px solid black;
	}
	</style>
</head>
<body>
<h1>Modal transport share in cities with more than a million people</h1>
<div id="nav">
	
</div>
<hr>
<h2 id="chart-title"></h2>
<div id="viz"></div>
<p><a href="https://en.wikipedia.org/wiki/Modal_share">Source: Wikipedia</a></p>
</body>
<script type="text/javascript">

var picto = {
	'walking':'🚶', 
	'cycling':'🚴',
	'public transport':'🚌',
	'private motor vehicle':'🚗'	
};

var categories = ['walking', 'cycling', 'public transport', 'private motor vehicle'];	
var width = 600, height = 150, margin = {top:20,left:10,bottom:0,right:10};	
var layout = d3_iconarray.layout()
					.height(5)
					.widthFirst(false);

var xScale = d3_iconarray.scale()
					.domain([0,20])
					.range([0, width-(margin.left+margin.right)])
					.gapSize(1)
					.gapInterval(5);

var yScale = d3.scaleLinear()
					.domain([0,5])
					.range([0,height-(margin.top + margin.bottom)])


d3.tsv('modal-share-data.tsv',function(data){
	console.log(data.columns)
	d3.select('#nav').selectAll('span.city-button')
		.data(data).enter()
			.append('span').attr('class','city-button')
			.append('button')
				.html(function(d){return d.City; })
				.on('click', function(d){
					draw(d);
				})


	draw(data.find(function(d){
		return (d.City == 'London');
	}));
})

function draw(data){

	d3.select('#chart-title').text(function(){
		return data.City + ' ('+data.year+')';
	});

	d3.select('#viz')
		.selectAll('svg')
		.data(categories)
			.enter()
		.append('svg')
			.attr('width', width)
			.attr('height', height)
				.attr('class','category')
			.append('g')
				.attr('class','plot')
				.attr('transform','translate('+margin.left+','+margin.top+')')

	d3.select('#viz')	
		.selectAll('svg')
		.classed('zero', function(d){
				return (data[d] == 0);
			});

	d3.selectAll('svg g.plot')
		.call(function(parent){
			var join = parent.selectAll('.icon')
				.data(function(d){ return layout( expand(data[d], d) ); });

			join.exit().remove();

			join.enter()
				.append('text').attr('class','icon');

			join.attr('transform', function(d){
				return 'translate(' + xScale(d.position.x) + ',' + yScale(d.position.y) + ')'
			}).text(function(d){ return picto[d.data]; })

		})
}

function expand(length, category){
	return d3.range(0,length,1).map(function(){ return category; })
}

</script>
</html>
(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-scale')) :
	typeof define === 'function' && define.amd ? define(['exports', 'd3-scale'], factory) :
	(factory((global.d3_iconarray = global.d3_iconarray || {}),global.d3));
}(this, function (exports,d3) { 'use strict';

	function iconArrayLayout() {
		var width = undefined;
		var height = undefined;
		var widthFirst = true;
		var maxDimension = undefined;

		function layout(data){
			//work our missing height, width stuff

			setDimensions(data.length);

			return data.map(function(d,i){
				return {
					data:d,
					position:position(i)
				};
			});
		}

		function position(i){
			if(isNaN(width) || isNaN(height)){ 
				console.log('Warning: width/height undefined') 
				return 0;
			}
			if(widthFirst){
				return {
					x: i % width,
					y: Math.floor( i/width )
				};
			}else{
				return {
					x: Math.floor( i/height ),
					y: i % height
				};
			}
		}

		function setDimensions(l){
			//neither width or height is defined
			if(isNaN(width) && isNaN(height)){
				console.log('no width or height');
				if(widthFirst){ 
					width = Math.ceil( Math.sqrt(l) );
					height = Math.ceil( l / width );
				}else{
					height = Math.ceil( Math.sqrt(l) );
					width = Math.ceil( l / height );
				}
			}else if(isNaN(width)){	//width undefined
				width = Math.ceil( l / height );
			}else if(isNaN(height)){ //height undefined
				height = Math.ceil( l / width );
			}
		}

		layout.maxDimension = function(x){
			var itemPosition = position(x); 
			if(widthFirst){
				var x = Math.max(itemPosition.x, width);
				return Math.max(x, itemPosition.y);
			}
			var y = Math.max(itemPosition.y, height);
			return Math.max(y, itemPosition.x);

		}

		layout.position = function(x){
			return position(x);
		}

		layout.width = function(x){
			if(x === undefined) return width;
			width = x;
			return layout;
		};

		layout.height = function(x){
			if(x === undefined) return height;
			height = x;
			return layout;
		};

		layout.widthFirst = function(b){
			if(b === undefined) return widthFirst;
			widthFirst = b;
			return layout;
		};

		return layout;
	};

	function iconArrayScale(){

		var domain = [0,100];
		var range = [0,100];
		var gapInterval = 10;
		var gapSize = 0; //default no change
		var notionalScale = d3.scaleLinear()
								.domain(domain)
								.range(range);

		function scale(domainValue){
			var rangeValue = 20;
			var adjustedDomainValue = domainValue + Math.floor(domainValue/gapInterval)*gapSize;
			//console.log(notionalScale.domain());
			return rangeValue = notionalScale(adjustedDomainValue);		
		}

		function rescale(){
			//calculate an adjusted domain
			var domainLength = (domain[1] - domain[0]) * gapSize;
			var gaps = Math.ceil( domainLength/ gapInterval );
			var adjustedDomain = [ domain[0], domain[1] + gaps ];

			//calculate an adjusted range

			notionalScale.domain(adjustedDomain)
					.range(range);
		}

		scale.gapInterval = function(x){
			if(!x) return gapInterval;
			gapInterval = x;
			rescale();
			return scale;
		};

		scale.gapSize = function(x){
			if(isNaN(x)) return gapSize;
			gapSize = x;
			rescale();
			return scale;
		}

		scale.domain = function(array){
			if(!array) return domain;
			domain = array;
			rescale();
			return scale;
		};

		scale.range = function(array){
			if(!array) return range;
			range = array;
			rescale();
			return scale;
		};

		rescale();
		return scale; 
	}

	var version = "0.0.1";

	exports.version = version;
	exports.layout = iconArrayLayout;
	exports.scale = iconArrayScale;

}));