Published
Edited
Oct 22, 2021
Importers
38 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// Computing the change in area for sorting
areas = new Map(
geoProposed.features
.map((d, i) => {
// Get corresponding proposed district
const current = geoCurrent.features[i];
const currArea = current ? path.area(current) : null;
const proposedArea = path.area(d);
const pctChange = (proposedArea - currArea) / currArea;
const absPctChange = Math.abs(pctChange);
return { district: d.properties.District, pctChange, absPctChange };
})
.sort((a, b) => a.pctChange - b.pctChange)
.map((d) => [
d.district,
{ pctChange: d.pctChange, absPctChange: d.absPctChange }
])
)
Insert cell
// Which cities to make visible on the inset
currentCities = cities.filter((d) => {
const [centerX, centerY] = path.centroid(selectedDistricts);
const [[x0, y0], [x1, y1]] = path.bounds(selectedDistricts);
const [x, y] = projection(d.coordinates);
const r = d3.max([centerX - x0, x1 - centerX, centerY - y0, y1 - centerY]);
return (
x > centerX - r && x < centerX + r && y > centerY - r && y < centerY + r
);
})
Insert cell
selectedDistricts = {
const proposed = geoProposed.features.filter(d => d.properties.District === selected)[0]
proposed.version = "proposed"
const current = geoCurrent.features.filter(d => d.properties.District === selected)[0]
const features = current ? [current, proposed] : [proposed]
return { type: "FeatureCollection", features}
}
Insert cell
cities = [
{label:"Houston", coordinates:[-95.3698, 29.7604]},
{label:"Austin", coordinates:[-97.7431, 30.2672]},
{label: "Dallas", coordinates:[-96.7970, 32.7767]},
{label: "San Antonio", coordinates:[-98.4936, 29.4241]},
{label: "El Paso", coordinates:[-106.4850, 31.7619]}
]
Insert cell
// Where to draw the circle on the full map
circleInfo = {
const [x, y] = path.centroid(selectedDistricts);
const [[x0, y0], [x1, y1]] = path.bounds(selectedDistricts)
const r= d3.max([x - x0, x1 - x, y - y0, y1 - y])
return {x, y, r}
}
Insert cell
// Which district is selected (updated by clicking)
mutable selected = 5
Insert cell
// Function to draw cities on the map, based on the cities and desired projections
drawCities = (wrapper, cities, projection) => {
const cityMarks = wrapper.selectAll("g.city").data(cities)
.join("g")
.style("pointer-events", "none")
.attr("transform", d => `translate(${projection(d.coordinates)[0]}, ${projection(d.coordinates)[1]})`)

cityMarks.append("circle")
.attr("r", width < 500 ? 2.5 : 3)

cityMarks.append("text")
.text(d => d.label)
.style("font-family", "sans-serif")
.attr("dx", 5)
.attr("dy", width < 500 ? 3 : 4)
.style("font-size", width < 500 ? 7 : 12)
.call(halo) // white background behind text
}
Insert cell
Insert cell
Insert cell
topoFiles = await FileAttachment("simplified (1).json").json()
Insert cell
// Displaying Plan C2101, as described here: https://data.capitol.texas.gov/dataset/planc2101
geoProposed = topojson.feature(topoFiles, topoFiles.objects.proposed)
Insert cell
geoCurrent = topojson.feature(topoFiles, topoFiles.objects.current)
Insert cell
// Path function for the large projection
largePath = d3.geoPath(largeProj)
Insert cell
// Projection for the inset
largeProj = d3.geoMercator().fitExtent(
[
[width / 10, 10],
[width / 2 - width / 10, height - 10]
],
selectedDistricts
)
Insert cell
// Path function for the state projection
path = d3.geoPath(projection)
Insert cell
// Projection for the full map
projection = d3.geoMercator().fitExtent(
[
[10, 10],
[width / 2 - 10, height - 10]
],
geoProposed
)
Insert cell
Insert cell
import {textcolor} from "@observablehq/text-color-annotations-in-markdown"
Insert cell
nCols = Math.floor(width / rectWidth)
Insert cell
height = (width /2)
Insert cell
radioLabels = ({district:"District number", pctChange: "Percent growth in area", absPctChange:"Percent change in area"})
Insert cell
import {drawOverlap} from "@observablehq/compute-overlapping-sections"
Insert cell
// White background for text
function halo(text) {
text
.clone(true)
.each(function () {
this.parentNode.insertBefore(this, this.previousSibling);
})
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-width", 4)
.attr("stroke-linejoin", "round");
}
Insert cell
// Creates the pattern for stripes
makeDef = () => {
return svg`<defs>
<pattern id="striped" patternUnits="userSpaceOnUse" width="3" height="3" patternTransform="rotate(45)">
<line x1="0" y="0" x2="0" y2="10" stroke="purple" stroke-width="2"></line>ƒ
</pattern>
</defs>`;
}
Insert cell
colors = ({current:"hsl(170 40% 80%)", proposed:"purple"})
Insert cell
// Spacing between charts
offset = 30
Insert cell
<style>
svg {
font-family:sans-serif;
overflow:visible;
}
path.outline:hover {
fill:#d3d3d3;
}
.wrapper {
display:flex;
overflow:visible;
}
path.outline {
stroke-width:0.4;
stroke:grey;
opacity:0.5;
mix-blend-mode:multiply;
cursor:pointer;
}

path.proposed_outline {
stroke:grey;
opacity:.7;
}
.striped {
display:inline;
padding:0px 5px;
border-radius:4px;
border:1px solid purple;
background: repeating-linear-gradient(-45deg,
transparent,
transparent 3px,
rgba(128,0,128,.25) 3px,
rgba(128,0,128,.25) 4px);
background-opacity:.1;
}
* {
box-sizing:border-box;
}
</style>
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more