Skip to content

Instantly share code, notes, and snippets.

@tristen
Created November 8, 2016 22:46
Show Gist options
  • Save tristen/f0fe27106340d9e7e454a91ab567a843 to your computer and use it in GitHub Desktop.
Save tristen/f0fe27106340d9e7e454a91ab567a843 to your computer and use it in GitHub Desktop.
Dynamic template
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title></title>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.26.0/mapbox-gl.css' rel='stylesheet' />
<link href='https://www.mapbox.com/base/latest/base.css' rel='stylesheet' />
<link href='site.css' rel='stylesheet' />
</head>
<body class='clip loading'>
<div id='map' class='map' />
<div class='pin-topleft pad1 z1'>
<div id='sidebar' class='sidebar round fill-dark dark round lifted' />
</div>
<script id='item-template' type='text/template'>
<div class='prose-big'>
<%- total %> <span class='small quiet'><%- percent %>%</span>
</div>
</script>
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.26.0/mapbox-gl.js'></script>
<script src='https://cdn.jsdelivr.net/lodash/4.16.6/lodash.min.js'></script>
<script src='site.js'></script>
</body>
</html>
body { margin:0; padding:0; }
.map {
position:absolute;
top:0;
bottom:0;
width:100%;
}
.sidebar {
width:200px;
}
.sidebar > div:last-child {
border-bottom:none;
}
.lifted { box-shadow:0 1px 2px rgba(0,0,0,0.20); }
/* Dark attribution */
.mapboxgl-ctrl.mapboxgl-ctrl-attrib { background-color:rgba(0,0,0,0.5); }
.mapboxgl-ctrl.mapboxgl-ctrl-attrib a { color:#fff; }
.rounded-toggle {
background-color:rgba(0,0,0,0.1);
background-repeat:no-repeat;
width:40px;
height:20px;
margin:0 auto;
padding:0;
position:relative;
border:0;
cursor:pointer;
}
.rounded-toggle:hover {
background-color:rgba(0,0,0,0.15);
}
.rounded-toggle:before {
content:'';
display:block;
background-color:#fff;
background-repeat:no-repeat;
position:absolute;
top:2px;
left:2px;
padding:8px;
border-radius:50%;
}
.rounded-toggle.active:before {
left:auto;
right:2px;
}
;
/* global mapboxgl, _*/
mapboxgl.accessToken = 'pk.eyJ1IjoidHJpc3RlbiIsImEiOiJiUzBYOEJzIn0.VyXs9qNWgTfABLzSI3YcrQ';
var sidebar = document.getElementById('sidebar');
var itemTemplate = _.template(document.getElementById('item-template').innerHTML);
var ethnicities = [{
group: 'Asian',
hex: '#3bb2d0'
}, {
group: 'Black',
hex: '#888BCA'
}, {
group: 'Hispanic',
hex: '#e55e5e'
}, {
group: 'White',
hex: '#fbb03b'
}, {
group: 'Other',
hex: '#ccc'
}];
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/dark-v9',
zoom: 12,
center: [-122.447303, 37.753574]
});
map.addControl(new mapboxgl.NavigationControl());
ethnicities.map(function(d) {
d.active = true;
d.stats = document.createElement('div');
var item = document.createElement('div');
item.className = 'keyline-bottom contain pad1';
var title = document.createElement('h4');
title.textContent = d.group;
var controls = document.createElement('div');
controls.className = 'pin-topright pad1';
var toggle = document.createElement('button');
toggle.className = 'rounded-toggle active';
toggle.style.backgroundColor = d.hex;
toggle.addEventListener('click', function() {
d.active = d.active ? false : true;
toggle.classList.toggle('active', d.active);
toggle.style.backgroundColor = d.active ? d.hex : 'rgba(255,255,255,0.2)';
map.setFilter('population', getFilter());
});
controls.appendChild(toggle);
item.appendChild(title);
item.appendChild(controls);
item.appendChild(d.stats);
sidebar.appendChild(item);
return d;
});
function getFilter() {
return ethnicities.filter(function(d) {
return d.active;
}).reduce(function(memo, d) {
memo.push(d.group);
return memo;
}, ['in', 'ethnicity']);
}
function renderStats() {
var features = map.querySourceFeatures('population', {
sourceLayer: 'sf2010',
filter: getFilter()
});
if (!features.length) return;
// Group features by ethnicity
features = _.groupBy(features, function(d) {
return d.properties.ethnicity;
});
var sum = Object.keys(features).reduce(function(memo, d) {
return memo + features[d].length;
}, 0);
ethnicities.forEach(function(d) {
var total = features[d.group] ? features[d.group].length : 0;
d.stats.innerHTML = itemTemplate({
total: total.toLocaleString(),
percent: (sum === 0) ? 0 : Math.floor(total / sum * 100)
});
});
}
map.on('load', function() {
map.addSource('population', {
type: 'vector',
url: 'mapbox://examples.8fgz4egr'
});
map.addLayer({
'id': 'population',
'type': 'circle',
'source': 'population',
'source-layer': 'sf2010',
'paint': {
'circle-radius': {
'base': 1.75,
'stops': [[12, 2], [22, 180]]
},
'circle-color': {
property: 'ethnicity',
type: 'categorical',
stops: ethnicities.map(function(d) {
return [d.group, d.hex];
})
}
}
}, 'water-label');
});
map.on('data', function(ev) {
if (ev.dataType === 'tile' && ev.source.id === 'population') {
document.body.classList.remove('loading');
renderStats();
}
});
@walkerke
Copy link

This is fantastic! How is your code licensed? I'd love to adapt it in a current project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment