Public
Edited
Mar 21, 2023
Insert cell
Insert cell
Insert cell
chart = addTooltips(
Plot.plot({
projection: "albers-usa",
marks: [
Plot.geo(states, { fill: "#dbdbdb", stroke: "white" }),
Plot.dot(power_plants, {
x: "longitude",
y: "latitude",
r: "total_capacity",
fill: "primary_source",
title: (d) => // Tooltip text
`Plant name: ${d.plant}\nPrimary source: ${
d.primary_source
}\nTotal capacity: ${d3.format(",")(d.total_capacity)} MW`,
opacity: 0.5
}),
Plot.text(
states.features,
Plot.centroid({
fill: "currentColor",
fontWeight: 600,
strokeWidth: 1.5,
stroke: "white",
filter: (d) => !pick.includes(d.properties.name), // Don't show the East Coast dodge 'pick' states
text: (d) => abbreviations.get(d.properties.name)
})
),
Plot.text(
states.features,
Plot.centroid({
textAnchor: "start",
dx: 10,
fill: "currentColor",
fontWeight: 600,
strokeWidth: 1.5,
stroke: "white",
filter: (d) => pick.includes(d.properties.name), // Show these East Coast pick states
text: (d) => abbreviations.get(d.properties.name)
})
),
legendBubble2([50, 500, 2000, 5000]), // Adding radius legend

sources.map(
// Adding legend with circle swatches
(source, i) => [
Plot.dot([1], {
frameAnchor: "left",
r: 7,
fill: [source],
dy: i * 20 - 45,
dx: 695
}),
Plot.text([1], {
frameAnchor: "left",
text: [source.replaceAll(/\b\w/g, (l) => l.toUpperCase())],
dy: i * 20 - 45,
dx: 705
})
]
)
],
r: { range: [0, maxBubbleSize] }, // Bubble size range
color: {
domain: sources,
range: [
"#f78a29", // natural gas - orange
"#535353", // coal - dark gray
"#ce499a", // nuclear - plum
"#027fc5", // hydroelectric - blue
"#ed1a25", // oil - red
"#11b14b", // wind - green
"#d5c843", // solar - chartreuse
"#ffecd7" // other - peach
]
},
height: Math.round(mapWidth * 5 / 8),
width: mapWidth,
marginRight: 60 // Add right margin to avoid cutting off text label
})
)
Insert cell
Insert cell
Insert cell
us = FileAttachment("us-counties-10m.json").json()
Insert cell
Insert cell
states = topojson.feature(us, us.objects.states)
Insert cell
Insert cell
Power_Plants.csv
SELECT
Longitude as longitude
, Latitude as latitude
, Plant_Name as plant
, City as city
, County as county
, StateName as state
, Total_MW as total_capacity
, (case when PrimSource = 'petroleum' then 'oil'
when PrimSource = 'geothermal' then 'other'
when PrimSource = 'biomass' then 'other'
when PrimSource = 'pumped storage' then 'other'
when PrimSource = 'batteries' then 'other'
else PrimSource end) as 'primary_source'
FROM "Power_Plants"
Insert cell
Insert cell
// Create a subset to dodge right (for some East Coast overlappers)
pick = ["Massachusetts", "New Hampshire", "Rhode Island", "Delaware"]
Insert cell
Insert cell
sources = [
"natural gas",
"coal",
"nuclear",
"hydroelectric",
"oil",
"wind",
"solar",
"other"
]
Insert cell
Insert cell
// ["District of Columbia", "D.C."] is deliberately ignored
abbreviations = FileAttachment("us_abbreviations.csv").csv()
.then((rows) => new Map(rows.map((d) => [d.state, d.standard_abbr])))
Insert cell
Insert cell
function legendBubble2(values) {
return (index, { r }) =>
Plot.plot({
axis: null,
marginTop: 35,
marginRight: Math.round(mapWidth * 35 / 800),
marginLeft: Math.round(mapWidth * 520 / 800),
width: Math.round(mapWidth * 660 / 800),
height: 100,
marks: [
Plot.text([1], {
text: ["Plant capacity by power source"],
frameAnchor: "top-right",
fontSize: 13,
fontWeight: 800,
dx: 35
}),
values.map((v, i) => [
Plot.dot(
[v],
Plot.dodgeY({
anchor: "top",
x: 5 * (i+2) ** 2,
dy: 28,
dx: -25,
r: r(v),
strokeWidth: 0.7,
stroke: "gray"
})
),
Plot.text([v], { frameAnchor: "top", x: 5 * (i+2) ** 2, dy: 18, dx: -25, text: d => `${d}${i===3 ? "MW":""}` , fontWeight: 500})
])
]
});
}
Insert cell
mapWidth = 1000
Insert cell
maxBubbleSize = Math.round(mapWidth * 17 / 800)
Insert cell
import {addTooltips} from "@mkfreeman/plot-tooltip"
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