SF Bay Area Jobs: Quantiles Visualizing the density of types of jobs in the 9 county SF Bay Area. Job categories are based on Robert Manduca's job dot map. The Census LEHD dataset breaks down job counts by 2-digit NAICS code. This represents the primary industry of the employing company. I aggregated these categories as follows: Manufacturing and Logistics: 11 (Agriculture and Forestry), 21 (Mining), 22 (Utilities), 23 (Construction), 31-33 (Manufacturing), 42 (Wholesale Trade), 48-49 (Transportation and Warehousing) Professional Services: 51 (Information), 52 (Finance and Insurance), 53 (Real Estate), 54 (Professional, Scientific, and Technical Services), 55 (Management of Companies and Enterprises) Healthcare, Education, and Government: 61 (Educational Services), 62 (Health Care), 81 (Other Services - largely Religious, Grantmaking, Civic, Professional, and Similar Organizations) Retail, Hospitality, and Other Services: 44-45 (Retail Trade), 56 (Administrative and Support Services), 71 (Arts, Entertainment, and Recreation - largely Amusement, Gambling, and Recreation), 72 (Accomodation and Food Services) Use your mouse wheel / trackpad to zoom and pan on the map.
Select a category and number of quantiles:
viewof category = html`<select>${Object.keys(geojson.features[0].properties) .filter(d => d !== "area_sqm") .map(d => `<option ${d === "profession" ? "selected" : null}>${d}</option>`)}</select>`
viewof numberOfQuantiles = html`<select>${d3.range(3, 10, 2).map(d => `<option ${d === 9 && 'selected'}>${d}</option>`)}</select>`
viewof svg = { const svg = d3.select(DOM.svg(width, height)) .style("width", "100%") .style("height", "auto") .call(zoom) // background svg.append("rect") .attr("width", width) .attr("height", height) .attr("x", 0) .attr("y", 0) .attr("fill", "hsl(0, 0%, 96%)") // choropleth const g = svg.append("g") .classed("map-layers", true) g.selectAll("path") .data(geojson.features) .enter().append("path") .attr("class", d => quantile(d.properties[category])) .attr("d", path) .append("title") .text(d => `${format(d.properties[category])} / sq meter`) g.append("path") .datum(topojson.mesh(tractsTopoJSON, tractsTopoJSON.objects.tracts_jobs_2002_wgs84)) .attr("fill", "none") .attr("stroke-width", 0.05) .attr("stroke", "purple") .attr("stroke-linejoin", "round") .attr("d", path); // legend const legend = svg.append("g") .classed("legend", true) .attr("transform", `translate(20, ${height - 60})`) legend.selectAll("rect") .data(quantile.range()) .enter().append("rect") .attr("height", 8) .attr("width", x.bandwidth()) .attr("x", d => x(d)) .attr("class", d => d) legend.append("g") .attr("transform", "translate(0, 5)") .call(d3.axisBottom(x)) legend.select(".domain").remove() legend.selectAll(".tick > line").remove() return svg.node() }
// california state plane 3 | https://github.com/veltman/d3-stateplane projection = d3.geoConicConformal() .parallels([37 + 4 / 60, 38 + 26 / 60]) .rotate([120 + 30 / 60], 0) .fitSize([width, height], geojson)
quantile = d3.scaleQuantile() .domain(values) .range(d3.range(numberOfQuantiles).map(i => `q${i}-9`))
x = d3.scaleBand() .domain(quantile.range()) .rangeRound([0, 300]) .padding(0.1)
format = d => +d3.format(".5f")(d)
tractsTopoJSON = d3.json("https://gist.githubusercontent.com/clhenrick/560b34f234cccba4ed884387c2c92cd5/raw/d21de2ffcc4d2345bbfae90dd7468b1b672b4b9c/tracts_jobs_2002_wgs84.json")
geojson = { let geojson = topojson.feature(tractsTopoJSON, tractsTopoJSON.objects.tracts_jobs_2002_wgs84) geojson.features = geojson.features.map(feature => { const total = Object.keys(feature.properties) .reduce((acc, cur) => cur !== "area_sqm" && cur !== "total" ? acc + feature.properties[cur] : acc, 0) feature.properties = {...feature.properties, total }; return feature; }) return geojson }
values = geojson.features.map(d => format(d.properties[category]))
path = d3.geoPath(projection)
function zoom (s) { s.call(d3.zoom() .on("zoom", () => s.select(".map-layers").attr("transform", d3.event.transform)) .scaleExtent([1, 18]) .translateExtent([[0, 0], [width, height]])) }
height = width * 1.2941176471
css = html`<style> /* for styling using d3.scaleQuantile */ .q0-9 { fill:rgb(247,251,255); } .q1-9 { fill:rgb(222,235,247); } .q2-9 { fill:rgb(198,219,239); } .q3-9 { fill:rgb(158,202,225); } .q4-9 { fill:rgb(107,174,214); } .q5-9 { fill:rgb(66,146,198); } .q6-9 { fill:rgb(33,113,181); } .q7-9 { fill:rgb(8,81,156); } .q8-9 { fill:rgb(8,48,107); } .legend text { font-size: 12px; fill: #333; } </style>`
dependencies
d3 = require("d3@next")
topojson = require("topojson")