Skip to content

Instantly share code, notes, and snippets.

@alansmithy
Last active April 26, 2019 06:17
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alansmithy/d832fc03f6e6a91e99f4 to your computer and use it in GitHub Desktop.
Save alansmithy/d832fc03f6e6a91e99f4 to your computer and use it in GitHub Desktop.
Pictogram grid in d3js
license: mit

Pictograms, or isotypes, in the form of repeating icons, can be used to effectively contrast quantity/magnitude comparisons in infographics and data visualisation. For more background information on the topic, I'd recommend the article by Robert Kosara:

The ISOTYPE may be close to 100 years old, but many of the ideas behind it are still very relevant and visualization might benefit from considering some of them.

Scalable Vector Graphics (SVG), the vector markup language exploited by d3.js, makes it easy to generate repeating graphics by declaring icon geometry once in the <defs> element and then calling them whenever needed via a <use> element. This simple example shows how to use d3 to create a grid of icons. A jQuery slider shows how those icons can then be addressed for interactive purposes.

The complex geometry needed to define an icon can be created by any vector graphics editor capable of generating SVG markup - Inkscape or Illustrator will work just fine. To see a project that uses this approach, have a look at the interactive census quiz of England & Wales.

<!doctype html>
<html>
<head>
<!--demonstration of using the svg 'use' element to create a pictogram-->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/themes/smoothness/jquery-ui.css" />
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js"></script>
<script src="//cdn.jsdelivr.net/jquery.ui.touch-punch/0.2.3/jquery.ui.touch-punch.min.js"></script>
<style type="text/css">
#sliderDiv {
margin:10px;
margin-top:30px;
height:15px;
width:300px;
}
svg {
overflow:none;
padding:10px;
float:left;
width:400px;
height:400px;
}
text {
fill:#bb6d82;
text-anchor:left;
font-size:12px;
font-family:sans-serif,Helvetica,Arial;
font-weight:bold;
}
.iconPlain {
fill:#a7a59b;
}
.iconSelected {
fill:#bb6d82;
}
rect {
fill:#fff1e0;
}
</style>
</head>
<body>
<script>
//placeholder div for jquery slider
d3.select("body").append("div").attr("id","sliderDiv");
//create svg element
var svgDoc=d3.select("body").append("svg").attr("viewBox","0 0 100 100");
//define an icon store it in svg <defs> elements as a reusable component - this geometry can be generated from Inkscape, Illustrator or similar
svgDoc.append("defs")
.append("g")
.attr("id","iconCustom")
.append("path")
.attr("d","M3.5,2H2.7C3,1.8,3.3,1.5,3.3,1.1c0-0.6-0.4-1-1-1c-0.6,0-1,0.4-1,1c0,0.4,0.2,0.7,0.6,0.9H1.1C0.7,2,0.4,2.3,0.4,2.6v1.9c0,0.3,0.3,0.6,0.6,0.6h0.2c0,0,0,0.1,0,0.1v1.9c0,0.3,0.2,0.6,0.3,0.6h1.3c0.2,0,0.3-0.3,0.3-0.6V5.3c0,0,0-0.1,0-0.1h0.2c0.3,0,0.6-0.3,0.6-0.6V2.6C4.1,2.3,3.8,2,3.5,2z");
//background rectangle
svgDoc.append("rect").attr("width",100).attr("height",100);
//specify the number of columns and rows for pictogram layout
var numCols = 10;
var numRows = 10;
//padding for the grid
var xPadding = 10;
var yPadding = 15;
//horizontal and vertical spacing between the icons
var hBuffer = 8;
var wBuffer = 8;
//generate a d3 range for the total number of required elements
var myIndex=d3.range(numCols*numRows);
//text element to display number of icons highlighted
svgDoc.append("text")
.attr("id","txtValue")
.attr("x",xPadding)
.attr("y",yPadding)
.attr("dy",-3)
.text("0");
//create group element and create an svg <use> element for each icon
svgDoc.append("g")
.attr("id","pictoLayer")
.selectAll("use")
.data(myIndex)
.enter()
.append("use")
.attr("xlink:href","#iconCustom")
.attr("id",function(d) {
return "icon"+d;
})
.attr("x",function(d) {
var remainder=d % numCols;//calculates the x position (column number) using modulus
return xPadding+(remainder*wBuffer);//apply the buffer and return value
})
.attr("y",function(d) {
var whole=Math.floor(d/numCols)//calculates the y position (row number)
return yPadding+(whole*hBuffer);//apply the buffer and return the value
})
.classed("iconPlain",true);
//create a jquery slider to control the pictogram
$( "#sliderDiv" ).slider({
orientation: "horizontal",
min: 0,
max: numCols*numRows,
value: 0,
slide: function( event, ui ) {
d3.select("#txtValue").text(ui.value);
d3.selectAll("use").attr("class",function(d,i){
if (d<ui.value) {
return "iconSelected";
} else {
return "iconPlain";
}
});
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment