Skip to content

Instantly share code, notes, and snippets.

@SeabassWells
Last active February 22, 2019 07:48
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 SeabassWells/b1b9642cc3465deaf29f845c406695a8 to your computer and use it in GitHub Desktop.
Save SeabassWells/b1b9642cc3465deaf29f845c406695a8 to your computer and use it in GitHub Desktop.
mappster
license: mit
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
{
"default": "Choose a dataset from the dropdown and then mouse over any neighborhood to explore.",
"popdensityperacre": "Number of people per acre in Thousands, 2016. Source: <a href='http://default.sfplanning.org/publications_reports/SF_NGBD_SocioEconomic_Profiles/2012-2016_ACS_Profile_Neighborhoods_Final.pdf' target='_blank'>American Community Survey</a>",
"percapitaincome": "Average income earned per person, 2016. Source: <a href='http://default.sfplanning.org/publications_reports/SF_NGBD_SocioEconomic_Profiles/2012-2016_ACS_Profile_Neighborhoods_Final.pdf' target='_blank'>American Community Survey</a>",
"percentnonwhite": "Non-White percent of population, 2016. Source: <a href='http://default.sfplanning.org/publications_reports/SF_NGBD_SocioEconomic_Profiles/2012-2016_ACS_Profile_Neighborhoods_Final.pdf' target='_blank'>American Community Survey</a>",
"percentinpoverty": "Percent of population with earnings below the poverty line, 2016. Source: <a href='http://default.sfplanning.org/publications_reports/SF_NGBD_SocioEconomic_Profiles/2012-2016_ACS_Profile_Neighborhoods_Final.pdf' target='_blank'>American Community Survey</a>",
"medianhomevalue": "Median home value, 2016. Source: <a href='http://default.sfplanning.org/publications_reports/SF_NGBD_SocioEconomic_Profiles/2012-2016_ACS_Profile_Neighborhoods_Final.pdf' target='_blank'>American Community Survey</a>",
"unemploymentrate": "Jobless share of the labor force, 2016. Source: <a href='http://default.sfplanning.org/publications_reports/SF_NGBD_SocioEconomic_Profiles/2012-2016_ACS_Profile_Neighborhoods_Final.pdf' target='_blank'>American Community Survey</a>",
"percapitacriminalarrests": "Rate of Criminal Arrests per 1,000 residents, 2018. Source: <a href='https://github.com/SeabassWells/understand-sf/tree/master/crime' target='_blank'>DataSF Crime Portal</a>",
"percapitaencampments": "Rate of 311 Encampment Reports per 1,000 residents, 2018. Source: <a href='https://github.com/SeabassWells/understand-sf/tree/master/homelessness' target='_blank'>DataSF 311 Portal</a>",
"medianhoursofsummerfog": "Average hours of summertime fog and low cloud cover, 1999-2009. Source: <a href='https://github.com/SeabassWells/understand-sf/tree/master/fog' target='_blank'>Climate Commons</a>",
"percentinliquefaction": "Percent of land area in a Liquefaction or Landslide zone, 2018. Source: <a href='https://github.com/SeabassWells/understand-sf/tree/master/liquefaction' target='_blank'>DataSF Seismic Hazard Zones</a>"
}
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
/* .counties {
fill: none;
} */
/* .states {
fill: none;
stroke-width: 20;
stroke: #9400D3;
stroke-linejoin: round;
} */
path {
stroke: #737373;
stroke-width: .1;
}
/* stroke: rgb(255, 255, 255);
stroke-width: .5px */
</style>
<!--
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script> -->
</head>
<body>
<div class="container-fluid">
<div class="row" style="padding-bottom: 35px;">
<div class="col-md-8 offset-md-2">
<h1 class="supreme"><a href="/" style="color: white;">&larr;</a> San Fran </h1>
</div>
</div>
<!-- begin content -->
<div class="row" style="padding-bottom: 25px;">
<div class="col-md-8 offset-md-2 blurb-row">
<p>The best way to understand the makeup of this country is data, not narrative. Pick which dataset is informative to you and see how it changes across states.</p>
<div class="dropdown">
<select class="select_box" id="opts">
<p></p>
<option value="default">Select a dataset</option>
<option value="popdensityperacre">Population Density</option>
<option value="percapitaincome">Income</option>
<option value="percentnonwhite">Racial Diversity</option>
<option value="percentinpoverty">Poverty</option>
<option value="medianhomevalue">Home Value</option>
<option value="unemploymentrate">Unemployment</option>
<option value="percapitacriminalarrests">Crime</option>
<option value="percapitaencampments">Homelessness</option>
<option value="medianhoursofsummerfog">Fog</option>
<option value="percentinliquefaction">Liquefaction</option>
</select>
</div>
</div>
</div>
<!-- viz row -->
<div class="row">
<div class="col-md-8 offset-md-2 viz-row">
</div>
</div>
</div>
<script src="w3color.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script type="text/javascript" src="http://d3js.org/d3.v4.min.js"></script>
<script type="text/javascript" src="main.js"></script>
</body>
//Alerts for browser compatibility
if (navigator.vendor == "Apple Computer, Inc." && !/Mobi|Android/i.test(navigator.userAgent)) {
alert("This visualization doesn't work very well in Safari. Try using Chrome if you can!");
}
if (/Mobi|Android/i.test(navigator.userAgent)) {
alert("This visualization doesn't work very well on mobile. Try using Chrome on Desktop if you can!")
}
var w = 1000;
var h = 650;
console.log("d3 executed!")
function cmykToRgb(c, m, y, k) {
var r, g, b;
r = Math.round(255 - ((Math.min(1, c * (1 - k) + k)) * 255));
g = Math.round(255 - ((Math.min(1, m * (1 - k) + k)) * 255));
b = Math.round(255 - ((Math.min(1, y * (1 - k) + k)) * 255));
return "rgb("+r+","+g+","+b+")";
}
//Create variable for updating dataset
var newData;
var svg = d3.select("body .viz-row")
.append("svg")
.attr("width", w)
.attr("height", h);
//Define path generator
var path = d3.geoPath()
var color = d3.scaleLinear()
.range([0,255])
var colorcmyk = d3.scaleLinear()
.range([1,0])
var tooltip = d3.select("body .viz-row").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
//Display data function
// load the dataset specified in the dropdown
function displayData(dataset) {
console.log("displayData executed on " + dataset);
// load the csv file with all the data
d3.csv("TheSFData.csv", function(data) {
//Set domain for color scale
color.domain([
d3.min(data, function(d) { return parseFloat(d[dataset]); }),
d3.max(data, function(d) { return parseFloat(d[dataset]); })
]);
colorcmyk.domain([
d3.min(data, function(d) { return parseFloat(d[dataset]); }),
d3.max(data, function(d) { return parseFloat(d[dataset]); })
]);
//Load in GeoJSON data with all the json
d3.json("bs.json", function(json) {
// json.transform.translate = [100,0]
var jsonClone = JSON.parse(JSON.stringify(json));
json = topojson.feature(json, json.objects.SFgeojson).features
jsonClone = topojson.feature(jsonClone, jsonClone.objects.SFgeojson)
for (var i = 0; i < data.length; i++) {
var dataState = data[i].nhood;
var dataValue = parseFloat(data[i][dataset]);
for (var j = 0; j < json.length; j++) {
var jsonState = json[j].properties.nhood;
if (dataState == jsonState) {
json[j].properties.value = dataValue;
// console.log(json[j].properties.value)
break;
}
}
}
//Bind data and create one path per GeoJSON feature
var projection = d3.geoIdentity()
.reflectY(true)
.fitSize([w,h],jsonClone)
var pathFlipped = d3.geoPath()
.projection(projection);
const mapSelection = svg.selectAll("path")
.data(json)
mapSelection
.enter()
.append("path")
.attr("d", pathFlipped)
.style("fill","lightgrey")
// .insertBefore(this,"text")
// .text(function(d){
// return d.properties.nhood;})
// .attr("x", function(d){
// return path.centroid(d)[0];})
// .attr("y", function(d){
// return path.centroid(d)[1];})
// .attr("text-anchor","middle")
// .attr('font-size','6pt');
// const MapLabels = mapSelection.selectAll("text")
// .data(json)
// .enter()
// var mapLabel = svg.selectAll("text")
// .data(json)
// .enter()
// .append("text")
// .text(function(d){
// return d.properties.nhood;})
// .attr("x", function(d){
// return path.centroid(d)[0];})
// .attr("y", function(d){
// return path.centroid(d)[1];})
// .attr("text-anchor","middle")
// .attr('font-size','6pt');
// .attr("transform", "translate(" + (getBBox().x + getBBox().width/2) + " " + (getBBox().y + getBBox().height/2) + ")")
// // .attr("x", function(d){
var mapLabel = svg.selectAll("text")
.data(json)
.enter()
.append("text")
.text(function(d){return d.properties.nhood;})
.attr('transform', function(d) { return 'translate(' + path.centroid(d) + ')'; })
//
// .attr("transform", function(d){
// console.log(path)
// console.log(path.getBBox(d).x);})
// .attr("y", function(d){
// return path.centroid(d)[1];})
// .attr("text-anchor","middle")
.attr('font-size','6pt');
// var mapLabel = svg.selectAll("text")
// .data(json)
// .enter()
// .append("text")
// .text(function(d){
// return d.properties.nhood;})
// .getBBox()
// .attr("transform", "translate(" + (getBBox().x + getBBox().width/2) + " " + (getBBox().y + getBBox().height/2) + ")")
// // .attr("x", function(d){
// // return path.centroid(d)[0];})
// // .attr("y", function(d){
// // return path.centroid(d)[1];})
// .attr("text-anchor","middle")
// .attr('font-size','6pt');
// var mapLabel = selectAll("text")
// .data(json)
// .enter()
// .append("text")
// .text(function(d){
// return d.properties.nhood
// });
// return d.properties.nhood;})
// .attr("x", function(d){
// return path.centroid(d)[0];})
// .attr("y", function(d){
// return path.centroid(d)[1];})
// .attr("text-anchor","middle")
// .attr('font-size','6pt');
//
// function addText(p)
// {
// var t = document.createElementNS("http://www.w3.org/2000/svg", "text");
// var b = p.getBBox();
// t.setAttribute("transform", "translate(" + (b.x + b.width/2) + " " + (b.y + b.height/2) + ")");
// t.setAttribute("fill", "red");
// t.setAttribute("font-size", "14");
// p.parentNode.insertBefore(t, p.nextSibling);
// }
//
// var paths = document.querySelectorAll("path");
// for (var p in paths)
// {
// addText(paths[p])
// }
mapSelection.transition()
.duration(900)
.style("fill", function(d) {
//Get data value
var value = d.properties.value;
if (value) {
console.log("1")
return cmykToRgb(.98,0,.15,colorcmyk(value));
} else {
//If default dataset
if (dataset == "default") {
console.log("2")
return "grey";
//If any other dataset
} else {
console.log("3")
return "#ccc";
}
}
});
mapSelection.on("mouseover", function(d) {
//Inject data value into paragraph
//Remove old text
d3.select(".blurb-row .dropdown #value-label")
.remove()
//Display new text
var paragraph = d3.select(".blurb-row .dropdown")
.append("p")
.text(function() {
//Based on the dataset, we'll return different tooltips (formatting in terms of percentages, etc.)
if (["percapitaincome", "medianhomevalue"].includes(dataset)) {
return (d.properties.nhood + ": $" + d.properties.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","));
} else if (["percentnonwhite", "percentinpoverty", "unemploymentrate",
"percentinliquefaction"].includes(dataset)){
return (d.properties.nhood + ": " + d.properties.value + "%");
} else if (["medianhoursofsummerfog"].includes(dataset)){
return (d.properties.nhood + ": " + d.properties.value + " hours");
} if (dataset=="default"){
return (d.properties.nhood);
} else {
return (d.properties.nhood + ": " + d.properties.value);
}
})
.attr("id", "value-label")
.classed("supreme", true)
.style("font-size", "1em")
.style("display", "inline")
.style("margin-left", "30px");
//Highlight current state
d3.select(this)
.transition()
.duration(300)
.style("opacity", .6);
})
.on("mouseout", function(d) {
//Remove old text
d3.select(".dropdown #value-label")
.remove()
//Return state to original opacity
d3.select(this)
.transition()
.duration(350)
.style("opacity", 1);
});
});
});
};
//Display text function
function displayInformation(dataset) {
//Load in dataset:description json file
d3.json("descriptions.json", function(json) {
var description = json[dataset];
//Remove old text
d3.select("#dataset-description")
.remove()
//Display new text
var paragraph = d3.select("body .blurb-row")
.append("p")
.html(description)
.attr("id", "dataset-description")
.style("font-size", "1em")
.style("font-family", "Futura, sans-serif")
.style("font-style", "italic");
});
}
//Load initial data and description
displayData("default");
displayInformation("default");
// handle on click event
d3.select('#opts')
.on('change', function() {
newData = d3.select(this).property('value');
displayInformation(newData);
displayData(newData);
});
nhood popdensityperacre percapitaincome percentnonwhite percentinpoverty medianhomevalue unemploymentrate totalarrests percapitacriminalarrests totalencampmentreports percapitaencampments medianhoursofsummerfog percentinliquefaction
Treasure Island 5 15886 66 51 -- 12 37 12 7 2 4 79
McLaren Park 2 15387 94 33 669643 26 34 40 19 22 7 4
Tenderloin 112 27946 57 30 509804 8 2589 92 3929 139 4 22
Lakeshore 8 22570 54 28 681159 16 418 29 24 2 8 14
Chinatown 103 24653 86 27 846774 7 542 37 141 10 4 49
South of Market 34 54202 62 27 752521 6 3613 188 12189 636 4 95
Japantown 47 68352 43 20 606250 5 281 77 128 35 4 0
Bayview Hunters Point 11 24817 86 19 566355 12 1543 41 1241 33 5 55
Western Addition 60 51264 57 17 636719 6 1188 54 2134 96 4 0
North Beach 39 60254 49 16 806903 7 1087 86 622 49 3 55
Visitacion Valley 47 20942 88 15 580231 10 393 21 28 2 7 2
Nob Hill 97 58623 45 14 685404 5 858 39 2957 133 4 1
Mission 49 53196 43 14 797306 6 3722 64 17388 297 5 33
Oceanview/Merced/Ingleside 42 26413 80 14 624528 10 354 13 84 3 8 1
Hayes Valley 58 61210 33 13 805046 5 875 48 644 35 4 13
Inner Richmond 47 56925 46 13 839002 6 466 21 314 14 6 0
Lone Mountain/USF 49 50860 38 10 904561 8 491 27 82 5 4 3
Outer Richmond 39 44745 56 10 880501 4 662 15 331 7 6 4
Sunset/Parkside 30 42430 65 10 829029 6 972 12 167 2 6 9
Russian Hill 56 91854 33 9 810158 3 718 40 146 8 4 12
Haight Ashbury 51 81392 20 9 908163 4 479 27 1166 65 4 7
Excelsior 44 28057 72 9 628628 8 530 14 82 2 6 2
Bernal Heights 38 53243 43 9 838307 7 549 21 557 21 6 11
Portola 31 29659 78 9 653611 7 349 21 157 10 7 7
Financial District/South Beach 24 114083 46 9 681493 5 2575 148 5215 299 4 61
Mission Bay 20 70287 58 9 754513 5 472 45 2141 203 4 93
Potrero Hill 19 84521 36 9 879765 6 720 52 2212 161 4 31
Presidio Heights 33 88517 31 8 913846 5 163 15 85 8 5 0
Castro/Upper Market 38 94317 22 7 907208 4 1030 49 4607 218 4 13
Outer Mission 38 32582 76 7 684176 8 450 19 129 5 6 4
Glen Park 19 72039 30 7 954665 5 133 16 79 10 6 20
Pacific Heights 47 102141 26 6 866733 4 554 23 631 26 4 0
West of Twin Peaks 20 67869 46 6 927384 4 622 16 60 2 7 7
Seacliff 18 117489 24 6 869565 5 14 6 3 1 6 12
Marina 38 98411 17 5 975000 4 741 30 576 23 4 51
Twin Peaks 18 64279 36 5 885714 4 84 11 30 4 4 16
Noe Valley 39 91014 22 4 934003 4 285 15 55 3 4 4
Presidio 3 86967 12 4 -- 1 8 2 9 2 5 19
Lincoln Park 1 43922 44 4 750000 10 10 31 9 28 5 38
Inner Sunset 32 63133 44 0.09 910805 4 343 12 124 4 6 11
Golden Gate Park 0.1 278 3089 203 2256 6 6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment