Skip to content

Instantly share code, notes, and snippets.

@cavedave
Last active January 30, 2016 12:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cavedave/993d26ae0ebe39447188 to your computer and use it in GitHub Desktop.
Save cavedave/993d26ae0ebe39447188 to your computer and use it in GitHub Desktop.
Gist to make a Voronoi map of Irish Breweries

Voronoi Maps

This project can be used to generate Voronoi maps using D3 and Leaflet. This code was made by Chris Zeeter and the data comes from Beoir.org. See an explanation of the code.

The code is released under the The MIT License.

body {
margin: 0;
font-family: Helvetica, Arial, sans-serif;
font-size: 14px;
}
p {
margin: 0;
margin-bottom: 10px;
}
.point-cell {
fill: none;
pointer-events: all;
stroke: #000;
stroke-opacity: .2;
}
.point-cell:hover, .point-cell.selected {
fill: none;
stroke: #000;
stroke-opacity: .6;
stroke-width: 2px;
}
.point-cell.selected {
stroke-opacity: 1;
stroke-width: 3px;
}
.point circle {
pointer-events: none;
}
#map {
position:absolute;
top:0;
bottom:0;
width:100%;
}
#selected,
#selections,
#loading:after,
#about {
position:absolute;
background-color: #FFF;
opacity: 0.8;
border-radius: 2px;
padding: 10px 10px 0 10px;
}
#about {
bottom: 10px;
right: 10px;
}
#about.visible {
width: 200px;
}
#about .hide {
padding-bottom: 0;
text-align: right;
}
#loading.visible:after {
top: 50%;
left: 50%;
height: 28px;
width: 80px;
margin-left: -50px;
margin-top: -30px;
content: 'drawing...';
font-size: 18px;
}
#selections {
right:10px;
top:10px;
width: 190px;
}
#selections label {
display: block;
padding-bottom: 8px;
}
#selections input[type=checkbox] {
position: relative;
top: -1px;
}
#selections .key {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 6px;
margin: 0 5px;
}
#selected {
bottom: 10px;
left: 10px;
height: 28px;
}
#selected h1 {
font-size: 20px;
margin: 0px;
line-height: 20px;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.hide,
.show {
padding-bottom: 10px;
display: block;
}
.content {
display: none;
}
@media (min-width: 480px) {
.selections .content {
display: block;
}
.selections .show {
display: none;
}
}
.hidden .content,
.visible .show {
display: none;
}
.hidden .show,
.visible .content {
display: block;
}
@media (max-width: 480px) {
#selected {
box-sizing: border-box;
width: 80%;
height: 32px;
}
#selected h1 {
font-size: 15px;
line-height: 15px;
font-weight: bold;
}
}
.mapbox-control-info {
display: none !important;
}
We can make this file beautiful and searchable if this error is corrected: It looks like row 2 should actually have 5 columns, instead of 6. in line 1.
type,color,name,latitude,longitude
beer,d8ac42,Carlow Brewing Company,52.697,-6.976,
beer,d8ac42,Trouble Brewing,53.252,-6.539,
beer,d8ac42,Inishmacsaint Brewing Company,54.421,-7.802,
beer,d8ac42,Beoir Chorca Dhuibhne (West Kerry Brewery),52.163,-10.423,
beer,d8ac42,Dungarvan Brewing Company,52.096,-7.632,
beer,d8ac42,Galway Hooker Brewery,53.281,-8.934,
beer,d8ac42,Hilden Brewery,54.52,-6.026,
beer,d8ac42,JW Sweetman,53.347,-6.258,
beer,d8ac42,Porterhouse Brewing Co.,53.403,-6.35,
beer,d8ac42,White Gypsy,52.793,-7.827,
beer,d8ac42,Whitewater Brewing Co.,54.088,-6.075,
beer,d8ac42,Galway Bay Brewery,53.289,-9.003,
beer,d8ac42,Metalman,52.257,-7.128,
beer,d8ac42,Bo Bristle,53.187,-7.989,
beer,d8ac42,Eight Degrees,52.276,-8.269,
beer,d8ac42,Burren Brewery,53.029,-9.291,
beer,d8ac42,Dingle Brewing Company,52.143,-10.263,
beer,d8ac42,Ards Brewing Company,54.556,-5.566,
beer,d8ac42,Kinnegar Brewing Company,55.108,-7.538,
beer,d8ac42,Donegal Brewing Company,54.503,-8.19,
cider,b9d842,Llewellyns Orchard,53.538,-6.189,
cider,b9d842,Armagh Cider Company,54.408,-6.496,
cider,b9d842,Stonewell Cider,51.737,-8.484,
cider,b9d842,Toby’s Handcrafted Armagh Cider,54.413,-6.49
cider,b9d842,Longueville House,52.138,-8.736,
cider,b9d842,Craigie’s Cider,52.998,-6.743,
cider,b9d842,Highbank,52.583,-7.315,
cider,b9d842,Tempted? Cider,54.509,-6.137,
cider,b9d842,Mac Ivors Cider Co.,54.446,-6.591,
cider,b9d842,Mac’s Armagh Cider,54.101,-6.446,
beer,d8ac42,Red Hand Brewing Co.,54.531,-6.813,
beer,d8ac42,West Mayo Brewery,53.818,-9.415,
beer,d8ac42,Blacks of Kinsale Craft Brewery,51.711,-8.516,
beer,d8ac42,Pokertree Brewing Company,54.599,-7.061,
beer,d8ac42,Brú Brewery,53.566,-6.799,
beer,d8ac42,Cotton Ball,51.914,-8.433,
beer,d8ac42,Munster Brewery,51.953,-7.869,
beer,d8ac42,Independent Brewing Company,53.26,-9.604,
beer,d8ac42,Farmageddon,54.5,-5.755,
beer,d8ac42,Jack Doyle's Brewery,52.443,-6.35,
beer,d8ac42,Carrig Brewing Company,54.047,-8.044,
cider,b9d842,Dan Kelly's Cider,53.718,-6.356,
beer,d8ac42,Hercules Brewing Company,54.632,-5.865,
beer,d8ac42,Mescan Brewery,53.772,-9.739,
beer,d8ac42,9 White Deer,51.936,-9.145,
cider,b9d842,Kilmegan Cider,54.265,-5.881,
beer,d8ac42,Hillstown Brewery,54.808,-6.353,
beer,d8ac42,Rascal's Brewing,53.299,-6.462,
beer,d8ac42,Sheelin Brewery and Kitchen,54.296,-7.641,
cider,b9d842,Cider Mill,53.713,-6.624,
beer,d8ac42,JJ's Brewery,52.406,-8.583,
cider,b9d842,Apple Farm,52.378,-7.842,
cider,b9d842,Dovehill Orchard,52.359,-7.479,
cider,b9d842,Long Meadow,54.42,-6.471,
beer,d8ac42,St. Mel's Brewery,53.731,-7.785,
beer,d8ac42,Brehon Brewhouse,53.981,-6.635,
beer,d8ac42,Kelly's Mountain Brew,53.302,-6.696,
beer,d8ac42,Rising Sons Brewery,51.9,-8.477,
beer,d8ac42,White Hag Brewery,54.089,-8.522,
beer,d8ac42,Jack Cody's Brewery,53.725,-6.324,
beer,d8ac42,Black Donkey Brewery,53.746,-8.643,
beer,d8ac42,Wicklow Brewery,52.889,-6.145,
beer,d8ac42,Wicklow Wolf Brewing,53.205,-6.107,
beer,d8ac42,Glens of Antrim Ales,55.213,-6.131,
beer,d8ac42,Rye River Brewery,53.337,-6.538,
beer,d8ac42,Reel Deel Brewery,54.101,-9.311,
beer,d8ac42,Elbow Lane,51.898,-8.468,
beer,d8ac42,West Cork Brewing Co.,51.485,-9.362,
beer,d8ac42,O Brother Brewing,53.1,-6.061,
beer,d8ac42,Knockout Brewing,54.58,-5.888,
beer,d8ac42,Raven Brew,53.457,-6.223,
beer,d8ac42,Torc Brewing,52.068,-9.429,
cider,b9d842,Boyne Valley Cider Company,53.566,-6.8,
beer,d8ac42,Drew Fox,52.329,-6.493,
beer,d8ac42,Barrahooley Brewery,54.966,-6.223,
beer,d8ac42,Northbound,55.035,-7.194,
beer,d8ac42,Killarney Brewing Company,52.054,-9.507,
beer,d8ac42,Boundary Brewing,54.599,-5.896,
beer,d8ac42,Walled City,54.998,-7.312,
beer,d8ac42,Yellow Belly Brewery,52.337,-6.461,
beer,d8ac42,Craftworks,53.372,-6.299,
beer,d8ac42,Corrib Brewing Company,53.416,-9.33,
beer,d8ac42,Mourne Mountains Brewery,54.113,-6.26,
beer,d8ac42,BrewBot,54.576,-5.917,
beer,d8ac42,Arthurstown Brewing Company,52.24,-6.942,
beer,d8ac42,Lacada Brewery,55.204,-6.652,
beer,d8ac42,Boyne Brewhouse,53.695,-6.368,
beer,d8ac42,Boghopper Brewery,55.066,-7.263,
beer,d8ac42,Mountain Man Brewing,51.9,-9.166,
beer,d8ac42,Western Herd Brewing,52.805,-9.098,
beer,d8ac42,Ó Cléirigh Brewing,53.886,-7.067,
beer,d8ac42,Five Lamps Brewery,53.333,-6.283,
beer,d8ac42,Franciscan Well Brewery,51.901,-8.481,
beer,d8ac42,Station Works Brewery,54.186,-6.369,
beer,d8ac42,Treaty City Brewing,52.668,-8.630,
beer,d8ac42,Kelly’s Mountain Brew,53.302,-6.695,
multi,6142d8,Guinness Brewery,53.344,-6.288,
multi,6142d8,C&C,52.359,-7.660,
multi,6142d8,Heineken,51.903,-8.472,
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="base.css" rel="stylesheet" />
<link href='https://api.tiles.mapbox.com/mapbox.js/v1.6.3/mapbox.css' rel='stylesheet' />
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id='map'>
</div>
<div id='selections' class="selections">
<a href='#' class="show">Choose what breweries to display</a>
<div class='content'>
<a href='#' class="hide">Hide</a>
<div id="toggles">
</div>
</div>
</div>
<div id='loading'>
</div>
<div id='selected'>
<h1>Explore Breweries in Ireland</h1>
</div>
<div id='about'>
<a href='#' class="show">About</a>
<p class='content'>
Explore Irish Breweries using a voronoi diagram. Each bubble are the locations where that brewery is closest.
Powered by data from <a href="http://www.beoir.org/">Beoir</a>, maps copyright
<a href='https://www.mapbox.com/about/maps/' target='_blank'>Mapbox and OpenStreetMap</a>.
<a href='#' class="hide">Hide</a>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js"></script>
<script src="https://api.tiles.mapbox.com/mapbox.js/v2.2.1/mapbox.js"></script>
<script src="voronoi_map.js"></script>
<script>
L.mapbox.accessToken = 'pk.eyJ1IjoiaWFtcmVkZGF2ZSIsImEiOiJjaWp5YXNlcnMwMDN2dmNtMGs3b3Z6N3VyIn0.45SLmNGTlgZGkbO_NRIfZQ';
var map = L.mapbox.map('map', 'mapbox.streets').setView([53.449, -8.311], 7);
url = 'brewery2.csv';
initialSelection = d3.set(['beer', 'cider']);
voronoiMap(map, url, initialSelection);
</script>
</body>
</html>
showHide = function(selector) {
d3.select(selector).select('.hide').on('click', function(){
d3.select(selector)
.classed('visible', false)
.classed('hidden', true);
});
d3.select(selector).select('.show').on('click', function(){
d3.select(selector)
.classed('visible', true)
.classed('hidden', false);
});
}
voronoiMap = function(map, url, initialSelections) {
var pointTypes = d3.map(),
points = [],
lastSelectedPoint;
var voronoi = d3.geom.voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
var selectPoint = function() {
d3.selectAll('.selected').classed('selected', false);
var cell = d3.select(this),
point = cell.datum();
lastSelectedPoint = point;
cell.classed('selected', true);
d3.select('#selected h1')
.html('')
.append('a')
.text(point.name)
.attr('href', point.url)
.attr('target', '_blank')
}
var drawPointTypeSelection = function() {
showHide('#selections')
labels = d3.select('#toggles').selectAll('input')
.data(pointTypes.values())
.enter().append("label");
labels.append("input")
.attr('type', 'checkbox')
.property('checked', function(d) {
return initialSelections === undefined || initialSelections.has(d.type)
})
.attr("value", function(d) { return d.type; })
.on("change", drawWithLoading);
labels.append("span")
.attr('class', 'key')
.style('background-color', function(d) { return '#' + d.color; });
labels.append("span")
.text(function(d) { return d.type; });
}
var selectedTypes = function() {
return d3.selectAll('#toggles input[type=checkbox]')[0].filter(function(elem) {
return elem.checked;
}).map(function(elem) {
return elem.value;
})
}
var pointsFilteredToSelectedTypes = function() {
var currentSelectedTypes = d3.set(selectedTypes());
return points.filter(function(item){
return currentSelectedTypes.has(item.type);
});
}
var drawWithLoading = function(e){
d3.select('#loading').classed('visible', true);
if (e && e.type == 'viewreset') {
d3.select('#overlay').remove();
}
setTimeout(function(){
draw();
d3.select('#loading').classed('visible', false);
}, 0);
}
var draw = function() {
d3.select('#overlay').remove();
var bounds = map.getBounds(),
topLeft = map.latLngToLayerPoint(bounds.getNorthWest()),
bottomRight = map.latLngToLayerPoint(bounds.getSouthEast()),
existing = d3.set(),
drawLimit = bounds.pad(0.4);
filteredPoints = pointsFilteredToSelectedTypes().filter(function(d) {
var latlng = new L.LatLng(d.latitude, d.longitude);
if (!drawLimit.contains(latlng)) { return false };
var point = map.latLngToLayerPoint(latlng);
key = point.toString();
if (existing.has(key)) { return false };
existing.add(key);
d.x = point.x;
d.y = point.y;
return true;
});
voronoi(filteredPoints).forEach(function(d) { d.point.cell = d; });
var svg = d3.select(map.getPanes().overlayPane).append("svg")
.attr('id', 'overlay')
.attr("class", "leaflet-zoom-hide")
.style("width", map.getSize().x + 'px')
.style("height", map.getSize().y + 'px')
.style("margin-left", topLeft.x + "px")
.style("margin-top", topLeft.y + "px");
var g = svg.append("g")
.attr("transform", "translate(" + (-topLeft.x) + "," + (-topLeft.y) + ")");
var svgPoints = g.attr("class", "points")
.selectAll("g")
.data(filteredPoints)
.enter().append("g")
.attr("class", "point");
var buildPathFromPoint = function(point) {
return "M" + point.cell.join("L") + "Z";
}
svgPoints.append("path")
.attr("class", "point-cell")
.attr("d", buildPathFromPoint)
.on('click', selectPoint)
.classed("selected", function(d) { return lastSelectedPoint == d} );
svgPoints.append("circle")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.style('fill', function(d) { return '#' + d.color } )
.attr("r", 4);
}
var mapLayer = {
onAdd: function(map) {
map.on('viewreset moveend', drawWithLoading);
drawWithLoading();
}
};
showHide('#about');
map.on('ready', function() {
d3.csv(url, function(csv) {
points = csv;
points.forEach(function(point) {
pointTypes.set(point.type, {type: point.type, color: point.color});
})
drawPointTypeSelection();
map.addLayer(mapLayer);
})
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment