Last active
August 29, 2015 14:03
-
-
Save nitaku/fc0db597d941956e6489 to your computer and use it in GitHub Desktop.
OpeNER - Places with categories (polar chart binning)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Setup | |
width = 960 | |
height = 500 | |
radius = 2 | |
svg = d3.select('svg') | |
# append a group for zoomable content | |
zoom_group = svg.append('g') | |
# define a zoom behavior | |
zoom = d3.behavior.zoom() | |
.scaleExtent([1,4]) # min-max zoom | |
.on('zoom', () -> | |
# whenever the user zooms, | |
# modify translation and scale of the zoom group accordingly | |
zoom_group.attr('transform', "translate(#{zoom.translate()})scale(#{zoom.scale()})") | |
# semantic zooming | |
zoom_group.selectAll('.place') | |
.attr('r', radius/zoom.scale()) | |
) | |
# bind the zoom behavior to the main SVG | |
svg.call(zoom) | |
# Lambert equal-area projection - EU standard for statistical maps | |
projection = d3.geo.azimuthalEqualArea() | |
.clipAngle(180 - 1e-3) | |
.scale(160000) | |
.rotate([-10.277475, -42.789034, 0]) | |
.translate([width / 2, height / 2]) | |
.precision(0.1) | |
path_generator = d3.geo.path() | |
.projection(projection) | |
categories = ['restaurant', 'attraction', 'poi', 'accommodation'] | |
classes = categories.length | |
colorify = d3.scale.category10() | |
.domain(categories) | |
data_url = 'http://wafi.iit.cnr.it/webvis/tmp/elba_places.json' | |
geo_url = 'http://wafi.iit.cnr.it/webvis/italiastat/data/istat/2011/reg2011.topo.json' | |
d3.json geo_url, (geo) -> | |
regions = topojson.feature(geo, geo.objects.reg2011) | |
zoom_group.selectAll('.region') | |
.data(regions.features) | |
.enter().append('path') | |
.attr('class', 'region') | |
.attr('d', path_generator) | |
d3.json data_url, (points) -> | |
# hexagonal binning | |
radius = 22 | |
apothem = Math.sqrt(3)/2 * radius | |
hexbin = d3.hexbin() | |
.size([width, height]) | |
.radius(radius) | |
.x((d) -> projection([d.lng, d.lat])[0] ) | |
.y((d) -> projection([d.lng, d.lat])[1] ) | |
bins = _.chain(hexbin(points)) | |
.forEach( (bin) -> | |
bin.classes_count = _.chain(categories) | |
.map( (klass) -> | |
count = bin.filter( (point) -> point.category is klass ).length | |
return { | |
count: count, | |
class: klass | |
} | |
) | |
.value() | |
) | |
.value() | |
max_tot = d3.max(bins, (bin) -> bin.length) | |
max = d3.max(bins, (bin) -> d3.max(bin.classes_count, (count) -> count.count) ) | |
angle = 2*Math.PI / classes | |
# polar area chart subplotting | |
subplots = zoom_group.selectAll('.subplot') | |
.data(bins) | |
subplots.enter().append('g') | |
.attr | |
class: 'subplot' | |
transform: (bin) -> "translate(#{bin.x},#{bin.y})" | |
radius_scale = d3.scale.linear() | |
.domain([0, max]) | |
.range([0, radius]) | |
arc_generator = d3.svg.arc() | |
.innerRadius(0) | |
.outerRadius((count) -> radius_scale(count.count)) | |
.startAngle((count, i) -> i*angle - angle/2) | |
.endAngle((count, i) -> i*angle + angle/2) | |
pies = subplots.selectAll('.pie') | |
.data((bin) -> bin.classes_count) | |
pies.enter().append('path') | |
.attr | |
class: 'pie' | |
d: arc_generator | |
fill: (count) -> colorify(count.class) | |
#zoom_group.selectAll('.place') | |
# .data(points) | |
# .enter().append('circle') | |
# .attr('class', 'place') | |
# .attr('r', radius) | |
# .attr('fill', (d) -> colorify(d.category) ) | |
# .attr('transform', (d) -> | |
# p = projection([d.lng, d.lat]) | |
# return "translate(#{p[0]},#{p[1]})" | |
# ) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
margin: 0; | |
padding: 0; | |
} | |
svg { | |
background: #BEDDF5; | |
} | |
.region { | |
fill: white; | |
} | |
.graticule { | |
fill: none; | |
stroke-width: 1px; | |
stroke: gray; | |
vector-effect: non-scaling-stroke; | |
} | |
.pie { | |
stroke: white; | |
stroke-width: 0.5; | |
vector-effect: non-scaling-stroke; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="description" content="OpeNER - Places with categories (polar chart binning)" /> | |
<title>OpeNER - Places with categories (polar chart binning)</title> | |
<link rel="stylesheet" href="index.css"> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="http://d3js.org/d3.hexbin.v0.min.js?5c6e4f0"></script> | |
<script src="http://d3js.org/topojson.v1.min.js"></script> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script> | |
</head> | |
<body> | |
<svg height="500" width="960"></svg> | |
<script src="index.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
var categories, classes, colorify, data_url, geo_url, height, path_generator, projection, radius, svg, width, zoom, zoom_group; | |
width = 960; | |
height = 500; | |
radius = 2; | |
svg = d3.select('svg'); | |
zoom_group = svg.append('g'); | |
zoom = d3.behavior.zoom().scaleExtent([1, 4]).on('zoom', function() { | |
zoom_group.attr('transform', "translate(" + (zoom.translate()) + ")scale(" + (zoom.scale()) + ")"); | |
return zoom_group.selectAll('.place').attr('r', radius / zoom.scale()); | |
}); | |
svg.call(zoom); | |
projection = d3.geo.azimuthalEqualArea().clipAngle(180 - 1e-3).scale(160000).rotate([-10.277475, -42.789034, 0]).translate([width / 2, height / 2]).precision(0.1); | |
path_generator = d3.geo.path().projection(projection); | |
categories = ['restaurant', 'attraction', 'poi', 'accommodation']; | |
classes = categories.length; | |
colorify = d3.scale.category10().domain(categories); | |
data_url = 'http://wafi.iit.cnr.it/webvis/tmp/elba_places.json'; | |
geo_url = 'http://wafi.iit.cnr.it/webvis/italiastat/data/istat/2011/reg2011.topo.json'; | |
d3.json(geo_url, function(geo) { | |
var regions; | |
regions = topojson.feature(geo, geo.objects.reg2011); | |
zoom_group.selectAll('.region').data(regions.features).enter().append('path').attr('class', 'region').attr('d', path_generator); | |
return d3.json(data_url, function(points) { | |
var angle, apothem, arc_generator, bins, hexbin, max, max_tot, pies, radius_scale, subplots; | |
radius = 22; | |
apothem = Math.sqrt(3) / 2 * radius; | |
hexbin = d3.hexbin().size([width, height]).radius(radius).x(function(d) { | |
return projection([d.lng, d.lat])[0]; | |
}).y(function(d) { | |
return projection([d.lng, d.lat])[1]; | |
}); | |
bins = _.chain(hexbin(points)).forEach(function(bin) { | |
return bin.classes_count = _.chain(categories).map(function(klass) { | |
var count; | |
count = bin.filter(function(point) { | |
return point.category === klass; | |
}).length; | |
return { | |
count: count, | |
"class": klass | |
}; | |
}).value(); | |
}).value(); | |
max_tot = d3.max(bins, function(bin) { | |
return bin.length; | |
}); | |
max = d3.max(bins, function(bin) { | |
return d3.max(bin.classes_count, function(count) { | |
return count.count; | |
}); | |
}); | |
angle = 2 * Math.PI / classes; | |
subplots = zoom_group.selectAll('.subplot').data(bins); | |
subplots.enter().append('g').attr({ | |
"class": 'subplot', | |
transform: function(bin) { | |
return "translate(" + bin.x + "," + bin.y + ")"; | |
} | |
}); | |
radius_scale = d3.scale.linear().domain([0, max]).range([0, radius]); | |
arc_generator = d3.svg.arc().innerRadius(0).outerRadius(function(count) { | |
return radius_scale(count.count); | |
}).startAngle(function(count, i) { | |
return i * angle - angle / 2; | |
}).endAngle(function(count, i) { | |
return i * angle + angle / 2; | |
}); | |
pies = subplots.selectAll('.pie').data(function(bin) { | |
return bin.classes_count; | |
}); | |
return pies.enter().append('path').attr({ | |
"class": 'pie', | |
d: arc_generator, | |
fill: function(count) { | |
return colorify(count["class"]); | |
} | |
}); | |
}); | |
}); | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment