Skip to content

Instantly share code, notes, and snippets.

@Andrew-Reid
Last active January 9, 2018 05:15
National Official Languages in the Americas

A more useful example of this attempt at a d3 module. Though I wouldn't advise on using so many discrete colors as I have here.

The example shows a more useful application of my attempt at a d3 module for patterns. The map is based off a visualization I probably first saw at age 6, in one of my favorite books, a 1989(?) topical world atlas - I'll find the name next visit my parents.

The map shows official languages (at a national level, sorry regional official languages) by country. Though, not all countries have official languages, so I've used a bit of discretion. Also, in the case of Bolivia, with a multitude of official languages, some of which may be extinct, I've shown some of the more major languages.

The map omits French Guiana - I quickly filtered countries by region, and French Guiana, as part of France didn't manage to stick around.

Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
d3.patternGetNextClass = function(svg) {
function increment() {
var selection = svg.select("#d3Pattern-"+i)
if (!selection.empty()) {
return false;
}
return true;
}
var i = 0;
while (!increment(i)) {
i++;
if ( i > 1000) break; // upper limit of patterns
}
return "d3Pattern-"+i;
}
d3.patternStripes = function(p,_) {
var colors = ["olive","white"];
var stripeWidth = 10;
var angle = 45;
var opacity = 1;
var id = null;
var svg = d3.select("svg");
if(p) svg = p;
if(_) id = _;
else id = d3.patternGetNextClass(svg);
function patternStripes() {
}
patternStripes.width = function(_) {
if(!_) return stripeWidth;
stripeWidth = _; this.add(); return patternStripes;
}
patternStripes.colors = function(_) {
if(!_) return colors;
colors = _; this.add(); return patternStripes;
}
patternStripes.opacity = function(_) {
if(!_) return opacity;
opacity = _; this.add(); return patternStripes;
}
patternStripes.angle = function(_) {
if(!_) return angle;
angle = _; this.add(); return patternStripes;
}
patternStripes.svg = function(_) {
if(!_) return svg;
svg = _; this.add(); return patternStripes;
}
patternStripes.add = function() {
var defs = svg.select("defs");
// add defs if not present
if(defs.empty()) defs = svg.append("defs");
// set width to an array if not already:
if (!Array.isArray(stripeWidth)) stripeWidth = [stripeWidth];
// set opacity to an array if not already:
if (!Array.isArray(opacity)) opacity = [opacity];
var totalWidth = 0;
var currentX = 0;
colors.forEach(function(d,i) {
totalWidth += stripeWidth[i%stripeWidth.length];
})
var pattern = svg.select("#"+id);
// append pattern if needed:
if (pattern.empty()) pattern = defs.append("pattern")
pattern
.attr("width", totalWidth)
.attr("height", 20) // arbitrary.
.attr("patternUnits","userSpaceOnUse")
.attr("patternTransform","rotate("+angle+")")
.attr("id",id);
var rects = pattern.selectAll("rect")
.data(colors);
rects.exit().remove();
var enter = rects.enter()
.append("rect");
enter.merge(rects)
.attr("width", function(d,i) { return stripeWidth[i%stripeWidth.length]; })
.attr("height", 20)
.attr("x", function(d,i) { currentX += stripeWidth[i%stripeWidth.length]; return currentX - stripeWidth[i%stripeWidth.length]; })
.attr("y",0)
.attr("fill",function(d,i) { return colors[i]; })
.attr("opacity",function(d,i) { return opacity[i%opacity.length]; })
return patternStripes;
}
patternStripes.use = function() {
return "url(#"+id+")";
}
patternStripes.add();
return patternStripes;
}
//////////////////////////////////////////////////////////////////////
// Plaid Pattern
/////////////////////////////////////////////////////////////////////
d3.patternPlaid = function(p,_) {
var colorsX = ["steelblue","white"];
var colorsY = ["darkblue","lightsteelblue"];
var widthX = 10;
var widthY = 10;
var opacityX = [0.3,0.7];
var opacityY = [0.3,0.7];
var angle = 45;
var id = null;
var svg = d3.select("svg");
if(p) svg = p;
if(_) id = _;
else id = d3.patternGetNextClass(svg);
function patternPlaid() {
}
patternPlaid.widthX = function(_) {
if(!_) return widthX;
widthX = _; this.add(); return patternPlaid;
}
patternPlaid.widthY = function(_) {
if(!_) return widthY;
widthY = _; this.add(); return patternPlaid;
}
patternPlaid.colorsX = function(_) {
if(!_) return colorsX;
colorsX = _; this.add(); return patternPlaid;
}
patternPlaid.colorsY = function(_) {
if(!_) return colorsY;
colorsY = _; this.add(); return patternPlaid;
}
patternPlaid.opacityX = function(_) {
if(!_) return opacityX;
opacityX = _; this.add(); return patternPlaid;
}
patternPlaid.opacityY = function(_) {
if(!_) return opacityY;
opacityY = _; this.add(); return patternPlaid;
}
patternPlaid.colorsY = function(_) {
if(!_) return colorsY;
colorsY = _; this.add(); return patternPlaid;
}
patternPlaid.angle = function(_) {
if(!_) return angle;
angle = _; this.add(); return patternPlaid;
}
patternPlaid.svg = function(_) {
if(!_) return svg;
svg = _; this.add(); return patternPlaid;
}
patternPlaid.add = function() {
var defs = svg.select("defs");
// add defs if not present
if(defs.empty()) defs = svg.append("defs");
// set width to an array if not already:
if (!Array.isArray(widthX)) widthX = [widthX];
if (!Array.isArray(widthY)) widthY = [widthY];
// set opacity to an array if not already:
if (!Array.isArray(opacityX)) opacityX = [opacityX];
if (!Array.isArray(opacityY)) opacityY = [opacityY];
var totalX = 0;
var currentX = 0;
var totalY = 0;
var currentY = 0;
colorsX.forEach(function(d,i) {
totalX += widthX[i%widthX.length];
})
colorsY.forEach(function(d,i) {
totalY += widthY[i%widthY.length];
})
var pattern = svg.select("#"+id);
// append pattern if needed:
if (pattern.empty()) pattern = defs.append("pattern")
pattern
.attr("width", totalX)
.attr("height", totalY)
.attr("patternUnits","userSpaceOnUse")
.attr("patternTransform","rotate("+angle+")")
.attr("id",id);
/////////// X
var rectsX = pattern.selectAll("rectX")
.data(colorsX);
rectsX.exit().remove();
var enter = rectsX.enter()
.append("rect")
.attr("class","rectX");
enter.merge(rectsX)
.attr("width", function(d,i) { return widthX[i%widthX.length]; })
.attr("height", 20)
.attr("x", function(d,i) { currentX += widthX[i%widthX.length]; return currentX - widthX[i%widthX.length]; })
.attr("y",0)
.attr("fill",function(d,i) { return colorsX[i]; })
.attr("opacity",function(d,i) { return opacityX[i%opacityX.length]; })
//////// Y
var rectsY = pattern.selectAll("rectY")
.data(colorsY);
rectsY.exit().remove();
var enter = rectsY.enter()
.append("rect")
.attr("class","rectY");
enter.merge(rectsY)
.attr("height", function(d,i) { return widthY[i%widthY.length]; })
.attr("width", 20)
.attr("y", function(d,i) { currentY += widthY[i%widthY.length]; return currentY - widthY[i%widthY.length]; })
.attr("x",0)
.attr("fill",function(d,i) { return colorsY[i]; })
.attr("opacity",function(d,i) { return opacityY[i%opacityY.length]; })
return patternPlaid;
}
patternPlaid.use = function() {
return "url(#"+id+")";
}
patternPlaid.add();
return patternPlaid;
}
//////////////////////////////////////////////////////////////////////
// Checker Pattern
/////////////////////////////////////////////////////////////////////
d3.patternCheckers = function(p,_) {
var colors = ["orange","white"]
var width = 10;
var angle = 0;
var opacity = 1;
var id = null;
var svg = d3.select("svg");
if(p) svg = p;
if(_) id = _;
else id = d3.patternGetNextClass(svg);
function patternCheckers() {
}
patternCheckers.width = function(_) {
if(!_) return width;
width = _; this.add(); return patternCheckers;
}
patternCheckers.colors = function(_) {
if(!_) return colors;
colors = _; this.add(); return patternCheckers;
}
patternCheckers.angle = function(_) {
if(!_) return angle;
angle = _; this.add(); return patternCheckers;
}
patternCheckers.opacity = function(_) {
if(!_) return opacity;
opacity = _; this.add(); return patternCheckers;
}
patternCheckers.svg = function(_) {
if(!_) return svg;
svg = _; this.add(); return patternCheckers;
}
patternCheckers.add = function() {
var defs = svg.select("defs");
// add defs if not present
if(defs.empty()) defs = svg.append("defs");
// set opacity to an array if not already:
if (!Array.isArray(opacity)) opacity = [opacity];
var totalWidth = width * 2;
var pattern = svg.select("#"+id);
// append pattern if needed:
if (pattern.empty()) pattern = defs.append("pattern")
pattern
.attr("width", totalWidth)
.attr("height", totalWidth)
.attr("patternUnits","userSpaceOnUse")
.attr("patternTransform","rotate("+angle+")")
.attr("id",id);
pattern.selectAll("rect").remove();
var rects = pattern.selectAll("rect")
.data(d3.range(4))
.enter()
.append("rect")
.attr("width", width )
.attr("height", width )
.attr("x", function(d,i) { return i%2 * width; })
.attr("y", function(d,i) { return Math.floor(i/2) * width; })
.attr("fill",function(d,i) { return (i == 0 || i == 3) ? colors[0] : colors[1]; } )
.attr("opacity", function(d,i) { return (i == 0 || i == 3) ? opacity[0] : opacity[1%opacity.length]; } )
return patternCheckers;
}
patternCheckers.use = function() {
return "url(#"+id+")";
}
patternCheckers.add();
return patternCheckers;
}
//////////////////////////////////////////////////////////////////////
// Dots Pattern
/////////////////////////////////////////////////////////////////////
d3.patternDots = function(p,_) {
var color = "steelblue";
var spacing = 5;
var radius = 1;
var angle = 0;
var opacity = 1;
var id = null;
var svg = d3.select("svg");
if(p) svg = p;
if(_) id = _;
else id = d3.patternGetNextClass(svg);
function patternDots() {
}
patternDots.spacing = function(_) {
if(!_) return spacing;
spacing = _; this.add(); return patternDots;
}
patternDots.color = function(_) {
if(!_) return color;
color = _; this.add(); return patternDots;
}
patternDots.radius = function(_) {
if(!_) return radius;
radius = _; this.add(); return patternDots;
}
patternDots.angle = function(_) {
if(!_) return angle;
angle = _; this.add(); return patternDots;
}
patternDots.opacity = function(_) {
if(!_) return opacity;
opacity = _; this.add(); return patternDots;
}
patternDots.svg = function(_) {
if(!_) return svg;
svg = _; return patternDots;
}
patternDots.add = function() {
var defs = svg.select("defs");
// add defs if not present
if(defs.empty()) defs = svg.append("defs");
var pattern = svg.select("#"+id);
// append pattern if needed:
if (pattern.empty()) pattern = defs.append("pattern")
pattern
.attr("width", spacing)
.attr("height", spacing)
.attr("patternUnits","userSpaceOnUse")
.attr("patternTransform","rotate("+angle+")")
.attr("id",id);
var circle = pattern.select("circle");
if(circle.empty()) circle = pattern.append("circle");
circle
.attr("cx", spacing/2 )
.attr("cy", spacing/2 )
.attr("r", radius)
.attr("fill", color);
return patternDots;
}
patternDots.use = function() {
return "url(#"+id+")";
}
patternDots.add();
return patternDots;
}
//////////////////////////////////////////////////////////////////////
// symbol
/////////////////////////////////////////////////////////////////////
d3.patternSymbols = function(p,_) {
var color = "crimson";
var stroke = "none";
var spacing = 16;
var angle = 0;
var path = d3.symbol().type(d3.symbolStar);
var id = null;
var svg = d3.select("svg");
if(p) svg = p;
if(_) id = _;
else id = d3.patternGetNextClass(svg);
function patternSymbols() {
}
patternSymbols.spacing = function(_) {
if(!_) return spacing;
spacing = _; this.add(); return patternSymbols;
}
patternSymbols.color = function(_) {
if(!_) return color;
color = _; this.add(); return patternSymbols;
}
patternSymbols.stroke = function(_) {
if(!_) return stroke;
stroke = _; this.add(); return patternSymbols;
}
patternSymbols.symbol = function(_) {
if(!_) return path;
path = _; this.add(); return patternSymbols;
}
patternSymbols.angle = function(_) {
if(!_) return angle;
angle = _; this.add(); return patternSymbols;
}
patternSymbols.svg = function(_) {
if(!_) return svg;
svg = _; this.add(); return patternSymbols;
}
patternSymbols.add = function() {
var defs = svg.select("defs");
// add defs if not present
if(defs.empty()) defs = svg.append("defs");
var pattern = svg.select("#"+id);
// append pattern if needed:
if (pattern.empty()) pattern = defs.append("pattern")
pattern
.attr("width", spacing)
.attr("height", spacing)
.attr("patternUnits","userSpaceOnUse")
.attr("patternTransform","rotate("+angle+")")
.attr("id",id);
var symbol = pattern.select("path");
if(symbol.empty()) symbol = pattern.append("path");
symbol
.attr("transform","translate("+[spacing/2,spacing/2]+ ")")
.attr("d", path )
.attr("stroke", stroke)
.attr("fill", color);
return patternSymbols;
}
patternSymbols.use = function() {
return "url(#"+id+")";
}
patternSymbols.add();
return patternSymbols;
}
//////////////////////////////////////////////////////////////////////
// Squares Pattern
/////////////////////////////////////////////////////////////////////
d3.patternSquares = function(p,_) {
var color = "steelblue";
var spacing = 10;
var length = 5;
var angle = 0;
var id = null;
var svg = d3.select("svg");
if(p) svg = p;
if(_) id = _;
else id = d3.patternGetNextClass(svg);
function patternSquares() {
}
patternSquares.spacing = function(_) {
if(!_) return spacing;
spacing = _; this.add(); return patternSquares;
}
patternSquares.color = function(_) {
if(!_) return color;
color = _; this.add(); return patternSquares;
}
patternSquares.edgeLength = function(_) {
if(!_) return length;
length = _; this.add(); return patternSquares;
}
patternSquares.angle = function(_) {
if(!_) return angle;
angle = _; this.add(); return patternSquares;
}
patternSquares.svg = function(_) {
if(!_) return svg;
svg = _; this.add(); return patternSquares;
}
patternSquares.add = function() {
var defs = svg.select("defs");
// add defs if not present
if(defs.empty()) defs = svg.append("defs");
var pattern = svg.select("#"+id);
// append pattern if needed:
if (pattern.empty()) pattern = defs.append("pattern")
pattern
.attr("width", spacing)
.attr("height", spacing)
.attr("patternUnits","userSpaceOnUse")
.attr("patternTransform","rotate("+angle+")")
.attr("id",id);
var rect = pattern.select("rect");
if(rect.empty()) rect = pattern.append("rect");
rect
.attr("x", spacing/2 - length/2)
.attr("y", spacing/2 - length/2)
.attr("width", length)
.attr("height", length)
.attr("fill", color);
return patternSquares;
}
patternSquares.use = function() {
return "url(#"+id+")";
}
patternSquares.add();
return patternSquares;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<svg width="620" height="960"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="d3Pattern.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var projection = d3.geoTransverseMercator()
.rotate([80,0])
.scale(350)
.center([0,10])
.translate([width / 2, height / 2]);
var path = d3.geoPath()
.projection(projection);
var languages = {
"English" : "steelblue" ,
"Spanish" : "lightseagreen" ,
"Portuguese" : "orange" ,
"French" : "mediumseagreen" ,
"Greenlandic":"darkgreen",
"Guarani":"fuchsia",
"Quecha":"yellow",
"Aymara":"pink",
"Chiquitano":"brown",
"Dutch": "lightsteelblue",
"Other/No Info" : "black"
}
var officialLanguages = {
"Anguilla":"English",
"Antigua and Barbuda":"English",
"Argentina":"Spanish",
"Aruba":"Dutch",
"Bahamas":"English",
"Barbados":"English",
"Belize":"English",
"Bolivia":["Spanish","Guarani","Quecha","Aymara","Chiquitano"],
"Brazil":"Portuguese",
"Canada": ["French","English"],
"Caymen Islands":"English",
"Chile":"Spanish",
"Colombia":"Spanish",
"Costa Rica":"Spanish",
"Cuba":"Spanish",
"Dominican Republic":"Spanish",
"Ecuador":"Spanish",
"El Salvador":"Spanish",
"French Guiana":"French",
"Grenada":"English",
"Guatemala":"Spanish",
"Guyana":"English",
"Haiti":["French","Creole"],
"Honduras":"Spanish",
"Jamaica":"English",
"Martinique":"French",
"Mexico":"Spanish",
"Nicaragua":"Spanish",
"Panama":"Spanish",
"Paraguay":["Spanish","Guarani"],
"Peru":["Spanish","Quecha"],
"Puerto Rico":["Spanish","English"],
"Suriname":"Dutch",
"Trinidad and Tobago":"English",
"United States of America":"English",
"Uruguay":"Spanish",
"Venezuela":"Spanish",
"Virgin Islans":"English",
"Greenland":"Greenlandic"
}
d3.json("Americas.json", function(error, json) {
if (error) throw error;
svg.selectAll()
.data(json.features)
.enter()
.append("path")
.attr("class", "boundary")
.attr("d", path )
.attr("fill", function(d) {
if (! Array.isArray( officialLanguages[d.properties.ADMIN] ) ) {
if( languages[officialLanguages[d.properties.ADMIN]] ) {
return languages[officialLanguages[d.properties.ADMIN]];
}
else {
return languages["Other/No info"];
}
}
else {
var array = officialLanguages[d.properties.ADMIN];
var length = array.length;
var width = 12 / length;
var colors = array.map(function(d) {
return languages[d];
})
var stripe = d3.patternStripes().colors(colors).width(width);
return stripe.use();
}
})
var legendEntry = svg.selectAll()
.data(d3.entries(languages))
.enter()
.append("g")
.attr("transform", function(d,i) {
return "translate(" + (width - 150) +"," + (300 + i * 16) + ")";
})
legendEntry.append("rect")
.attr("width", 12)
.attr("height",12)
.attr("fill", function(d) { return d.value; })
.attr("stroke","black")
.attr("stroke-width",1)
legendEntry.append("text")
.attr("x", 16)
.attr("y", 10)
.text(function(d) { return d.key; })
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment