forked from Jay-Oh-eN's block: Civic Impact through Data Visualization: Final
forked from mansweet's block: Civic Impact through Data Visualization: Final
forked from Jay-Oh-eN's block: Civic Impact through Data Visualization: Final
forked from mansweet's block: Civic Impact through Data Visualization: Final
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.8/d3.min.js"></script> | |
| <style> | |
| body, html { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| svg { | |
| width:50%; | |
| height:100%; | |
| float: left; | |
| } | |
| circle.airbnb { | |
| fill: #e00007; | |
| opacity: 0.6; | |
| } | |
| .axis { | |
| font-family: arial; | |
| font-size: 0.7em; | |
| } | |
| text { | |
| fill: black; | |
| } | |
| .label { | |
| font-size: 2em; | |
| } | |
| path { | |
| fill: none; | |
| stroke: black; | |
| stroke-width: 2px; | |
| } | |
| .tick { | |
| fill: none; | |
| stroke: black; | |
| } | |
| circle { | |
| opacity: 0.9; | |
| stroke: none; | |
| fill: red; | |
| } | |
| .line { | |
| fill: none; | |
| stroke: #e00007; | |
| stroke-width: 1px; | |
| } | |
| </style> | |
| <script> | |
| function draw(geo_data) { | |
| "use strict"; | |
| /* | |
| D3.js setup code | |
| */ | |
| var margin = 75, | |
| width = 750 - margin, | |
| height = 780 - margin; | |
| // https://github.com/mbostock/d3/wiki/Time-Formatting | |
| var format = d3.time.format("%Y-%m-%d"); | |
| var projection = d3.geo.mercator() | |
| .center([-122.433701, 37.767683]) | |
| .scale(230000) | |
| .translate([width / 1.95, height / 1.74]); | |
| var path = d3.geo.path() | |
| .projection(projection); | |
| var map = d3.select('#map').selectAll('path') | |
| .data(geo_data.features) | |
| .enter() | |
| .append('path') | |
| .attr('d', path) | |
| .style('fill', '#eee') | |
| .style('stroke', 'black') | |
| .style('stroke-width', 1); | |
| map.datum(function(d) { | |
| var normalized = d.properties.neighbourhood | |
| .replace(/ /g, '_') | |
| .replace(/\//g, '_'); | |
| d.properties.neighbourhood = normalized; | |
| return d; | |
| }); | |
| map.attr('class', function(d) { | |
| return d.properties.neighbourhood; | |
| }); | |
| d3.json("http://cdn.rawgit.com/hopelessoptimism/interactive-data-viz/master/data/airbnb/listing_count.json", function(data) { | |
| var listings_extent = d3.extent(d3.values(data)); | |
| var bubbles = d3.select('#map').append("g") | |
| .attr("class", "bubble") | |
| .selectAll("circle") | |
| .data(geo_data.features) | |
| .enter() | |
| .append("circle") | |
| .attr('class', 'airbnb'); | |
| bubbles.datum(function(d) { | |
| d.count = data[d.properties.neighbourhood]; | |
| return d; | |
| }); | |
| var radius = d3.scale.pow().exponent(1.2) | |
| .domain(listings_extent) | |
| .range([3, 25]); | |
| bubbles | |
| .attr("cx", function(d) { return path.centroid(d.geometry)[0]; }) | |
| .attr("cy", function(d) { return path.centroid(d.geometry)[1]; }) | |
| .attr("r", function(d) { return radius(d.count); }); | |
| d3.csv('http://cdn.rawgit.com/hopelessoptimism/interactive-data-viz/master/data/airbnb/neighborhood_reviews_timeseries.csv', | |
| function(timeseries) { | |
| var field = "Mission"; | |
| // maximum reviews | |
| var max_y = d3.max(timeseries, function(d) { | |
| var max = 0; | |
| d3.values(d).forEach(function(i) { | |
| if (+i && (+i > max)) { | |
| max = +i; | |
| } | |
| }); | |
| return max; | |
| }); | |
| // Create y-axis scale mapping price -> pixels | |
| var measure_scale = d3.scale.linear() | |
| .range([height, 100]) | |
| .domain([0, max_y]); | |
| // Create D3 axis object from measure_scale for the y-axis | |
| var measure_axis = d3.svg.axis() | |
| .scale(measure_scale) | |
| .orient("right"); | |
| // Append SVG to page corresponding to the D3 y-axis | |
| d3.select('#chart').append('g') | |
| .attr('class', 'y axis') | |
| .attr("transform", "translate(" + (width - 40) + " ,0)") | |
| .call(measure_axis); | |
| // add label to y-axis | |
| d3.select(".y.axis") | |
| .append("text") | |
| .attr('class', 'label') | |
| .text("Reviews per week") | |
| .attr("transform", "translate(45,300) rotate(90)"); | |
| var drawChart = function(field) { | |
| d3.select('#chart').select('.x.axis').remove(); | |
| d3.select('#chart').select('#chart path').remove(); | |
| d3.select('#heading') | |
| .text(field); | |
| // remove missing values | |
| timeseries = timeseries.filter(function(d) { | |
| return d[field]; | |
| }); | |
| // get min/max dates | |
| var time_extent = d3.extent(timeseries, function(d){ | |
| return format.parse(d['timestamp']); | |
| }); | |
| // Create x-axis scale mapping dates -> pixels | |
| var time_scale = d3.time.scale() | |
| .range([0, width - 50]) | |
| .domain(time_extent); | |
| // Create D3 axis object from time_scale for the x-axis | |
| var time_axis = d3.svg.axis() | |
| .scale(time_scale) | |
| .tickFormat(d3.time.format("%b '%y")); | |
| // Append SVG to page corresponding to the D3 x-axis | |
| d3.select('#chart').append('g') | |
| .attr('class', 'x axis') | |
| .attr('transform', "translate(0," + height + ")") | |
| .call(time_axis); | |
| // define the values to map for x and y position of the line | |
| var line = d3.svg.line() | |
| .x(function(d) { return time_scale(format.parse(d['timestamp'])); }) | |
| .y(function(d) { return measure_scale(+d[field]); }); | |
| // append a SVG path that corresponds to the line chart | |
| d3.select('#chart').append("path") | |
| .datum(timeseries) | |
| .attr("class", "line") | |
| .attr("d", line); | |
| }; | |
| drawChart(field); | |
| var mover = function(d) { | |
| var neigh = d.properties.neighbourhood; | |
| d3.select('#map path.' + neigh).style('fill', 'black'); | |
| drawChart(neigh); | |
| }; | |
| var mout = function(d) { | |
| var neigh = d.properties.neighbourhood; | |
| d3.select('path.' + neigh).style('fill', '#eee'); | |
| } | |
| map.on("mouseover", mover); | |
| map.on("mouseout", mout); | |
| bubbles.on('mouseover', mover); | |
| bubbles.on('mouseout', mout); | |
| }); | |
| }); | |
| } | |
| </script> | |
| </head> | |
| <body> | |
| <svg id="map"></svg> | |
| <svg id="chart"> | |
| <text x="50%" y="50" id="heading" font-size="2em" text-anchor="middle" font-family="futura">SF</text> | |
| </svg> | |
| <script> | |
| d3.json("http://cdn.rawgit.com/hopelessoptimism/interactive-data-viz/master/data/airbnb/neighbourhoods.geojson", draw); | |
| </script> | |
| </body> | |
| </html> |