Skip to content

Instantly share code, notes, and snippets.

@cavedave
Last active January 1, 2020 09:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cavedave/707d2d480c1ff018948e07cd22e609ea to your computer and use it in GitHub Desktop.
Save cavedave/707d2d480c1ff018948e07cd22e609ea to your computer and use it in GitHub Desktop.
Voronoi of Dublin supermarkets.
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,CHATHAM STREET,53.340,-6.262,
beer,d8ac42,BLESSINGTON STREET,53.356,-6.268,
beer,d8ac42,BOLTON STREET,53.351,-6.269,
beer,d8ac42,GREEK STREET,53.346,-6.272,
beer,d8ac42,CHARLEMONT PLACE,53.330,-6.260,
beer,d8ac42,CHRISTCHURCH PLACE,53.343,-6.270,
beer,d8ac42,HIGH STREET,53.343,-6.275,
beer,d8ac42,CUSTOM HOUSE QUAY,53.347,-6.248,
beer,d8ac42,EXCHEQUER STREET,53.343,-6.263,
beer,d8ac42,DAME STREET,53.344,-6.266,
beer,d8ac42,EARLSFORT TERRACE,53.334,-6.258,
beer,d8ac42,ECCLES STREET,53.359,-6.269,
beer,d8ac42,FITZWILLIAM SQUARE WEST,53.336,-6.252,
beer,d8ac42,FOWNES STREET UPPER,53.344,-6.263,
beer,d8ac42,HARDWICKE STREET,53.355,-6.264,
beer,d8ac42,GEORGES QUAY,53.347,-6.252,
beer,d8ac42,GOLDEN LANE,53.340,-6.267,
beer,d8ac42,GRANTHAM STREET,53.334,-6.265,
beer,d8ac42,HERBERT PLACE,53.334,-6.245,
beer,d8ac42,JAMES STREET EAST,53.336,-6.248,
beer,d8ac42,LEINSTER STREET SOUTH,53.34218,-6.254,
beer,d8ac42,TOWNSEND STREET,53.345,-6.254,
beer,d8ac42,CUSTOM HOUSE,53.348,-6.254,
beer,d8ac42,CATHAL BRUGHA STREET,53.352,-6.260,
beer,d8ac42,MERRION SQUARE EAST,53.339,-6.246,
beer,d8ac42,MERRION SQUARE WEST,53.339,-6.251,
beer,d8ac42,MOLESWORTH STREET,53.341,-6.258,
beer,d8ac42,MOUNTJOY SQUARE WEST,53.356,-6.258,
beer,d8ac42,ORMOND QUAY UPPER,53.346,-6.268,
beer,d8ac42,PARNELL SQUARE NORTH,53.353,-6.265,
beer,d8ac42,PARNELL STREET,53.350,-6.265,
beer,d8ac42,PEARSE STREET,53.344,-6.250,
beer,d8ac42,PRINCES STREET / O'CONNELL STREET,53.349,-6.260,
beer,d8ac42,PORTOBELLO HARBOUR,53.330,-6.265,
beer,d8ac42,SMITHFIELD,53.347,-6.278,
beer,d8ac42,ST. STEPHEN'S GREEN EAST,53.337,-6.256,
beer,d8ac42,ST. STEPHEN'S GREEN SOUTH,53.337,-6.261,
beer,d8ac42,TALBOT STREET,53.350,-6.252,
beer,d8ac42,WILTON TERRACE,53.332,-6.252,
beer,d8ac42,JERVIS STREET,53.3483,-6.266,
beer,d8ac42,HARCOURT TERRACE,53.332,-6.257,
beer,d8ac42,SMITHFIELD NORTH,53.349,-6.278,
beer,d8ac42,PORTOBELLO ROAD,53.330,-6.268,
beer,d8ac42,UPPER SHERRARD STREET,53.358,-6.260,
beer,d8ac42,DEVERELL PLACE,53.351,-6.255,
beer,d8ac42,STRAND STREET GREAT,53.347,-6.264,
beer,d8ac42,EXCISE WALK,53.347,-6.244,
beer,d8ac42,GUILD STREET,53.347,-6.240,
beer,d8ac42,YORK STREET WEST,53.339,-6.264,
beer,d8ac42,YORK STREET EAST,53.338,-6.262,
beer,d8ac42,NEWMAN HOUSE,53.337,-6.260,
beer,d8ac42,CLONMEL STREET,53.336,-6.262,
beer,d8ac42,HATCH STREET,53.33403,-6.260,
beer,d8ac42,MOUNT STREET LOWER,53.33796,-6.241,
beer,d8ac42,GRATTAN STREET,53.339,-6.243,
beer,d8ac42,SIR PATRICK DUN'S,53.339,-6.240,
beer,d8ac42,DENMARK STREET GREAT,53.35561,-6.261,
beer,d8ac42,NORTH CIRCULAR ROAD,53.359,-6.260,
beer,d8ac42,HARDWICKE PLACE,53.357,-6.263,
beer,d8ac42,LIME STREET,53.346,-6.243,
beer,d8ac42,FENIAN STREET,53.341,-6.246,
beer,d8ac42,SANDWITH STREET,53.345,-6.247,
beer,d8ac42,CONVENTION CENTRE,53.34744,-6.238,
beer,d8ac42,NEW CENTRAL BANK,53.347,-6.234,
beer,d8ac42,THE POINT,53.346,-6.230,
beer,d8ac42,HANOVER QUAY,53.344,-6.237,
beer,d8ac42,GRAND CANAL DOCK,53.342,-6.238,
beer,d8ac42,BARROW STREET,53.341,-6.236,
beer,d8ac42,KEVIN STREET,53.337,-6.267,
beer,d8ac42,JOHN STREET WEST,53.343,-6.277,
beer,d8ac42,FRANCIS STREET,53.342,-6.275,
beer,d8ac42,OLIVER BOND STREET,53.343,-6.280,
beer,d8ac42,JAMES STREET ,53.343,-6.287,
beer,d8ac42,MARKET STREET SOUTH,53.342,-6.287,
beer,d8ac42,WOLFE TONE STREET,53.348,-6.267,
beer,d8ac42,MATER HOSPITAL,53.359,-6.264,
beer,d8ac42,ECCLES STREET EAST,53.358,-6.265,
beer,d8ac42,ST. JAMES HOSPITAL (LUAS),53.341,-6.292,
beer,d8ac42,ST. JAMES HOSPITAL (CENTRAL),53.339,-6.295,
beer,d8ac42,MOUNT BROWN,53.341,-6.297,
beer,d8ac42,EMMET ROAD,53.340,-6.308,
beer,d8ac42,BROOKFIELD ROAD,53.339,-6.300,
beer,d8ac42,ROTHE ABBEY,53.338,-6.30395,
beer,d8ac42,PARKGATE STREET,53.347,-6.291,
beer,d8ac42,COLLINS BARRACKS MUSEUM,53.347,-6.285,
beer,d8ac42,BLACKHALL PLACE,53.3488,-6.281,
beer,d8ac42,FITZWILLIAM SQUARE EAST,53.335,-6.2509,
beer,d8ac42,BENSON STREET,53.344,-6.233,
beer,d8ac42,SOUTH DOCK ROAD,53.341,-6.231,
beer,d8ac42,HEUSTON BRIDGE (NORTH),53.347,-6.292,
beer,d8ac42,HEUSTON STATION (CENTRAL),53.346,-6.296,
beer,d8ac42,HEUSTON STATION (CAR PARK),53.346,-6.297,
beer,d8ac42,ROYAL HOSPITAL,53.343,-6.297,
beer,d8ac42,KILMAINHAM LANE,53.341,-6.305,
beer,d8ac42,KILMAINHAM GAOL,53.342,-6.310,
beer,d8ac42,FREDERICK STREET SOUTH,53.341,-6.256,
beer,d8ac42,CITY QUAY,53.346,-6.246,
beer,d8ac42,HEUSTON BRIDGE (SOUTH),53.347,-6.292,
beer,d8ac42,KING STREET NORTH,53.350,-6.273,
beer,d8ac42,WESTERN WAY,53.354,-6.269,
<!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">
<link rel="shortcut icon" href="">
</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>Dublin Bikes</h1>
</div>
<div id='about'>
<a href='#' class="show">About</a>
<p class='content'>
Explore Dublin Bikes using a voronoi diagram. Each bubble are the locations where that bike station is closest.
Powered by data from https://data.gov.ie/dataset/dublinbikes, 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.34, -6.21], 10);
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