Some background
Every year, the Presbyterian Church in America (PCA) meets at what is called a “General Assembly”. Overtures (amendments) are submitted and voted on. If an amendment gets a majority of “Yeas”, then it goes to be voted on by each presbytery. If an amendment passes two-thirds of presbyteries, then it is voted on again at the next General Assembly. If it passes in this stage, the amendment is adopted.
This notebook can also be viewed on ObservableHQ.
Data comes from @SEdburg and can be found in his Google Drive sheet
Code for keeping the data in sync with Scott’s spreadsheet lives in my pca-amendments GitHub repository. A small Python script uses pandas
to read from Scott’s spreadsheet, prepares the data for this notebook, and then outputs it into the same repository. This script is initalized through GitHub Actions.
Vote tracking
Change boundary type to see the vote results change on the map. Select an item to see the vote counts for each item (results reflected in the bar chart and map).
Code
Code
Code
.plot({
Plotx: {
grid: true,
label: 'vote count'
,
}y: {
label: null
,
}marks: [
// Plot.line([59], {fillOpacity: 1, stroke: 'black'}),
// Plot.barX([59], {fillOpacity: 0.4, stroke: 'black'}),
// Plot.barX([88], {fill: "orange", fillOpacity: 0.4}),
.barX(votes, {x: "count", y: "vote", fill: d => d.vote === 'for' ? forColor : againstColor}),
Plot.ruleX([59], {stroke: "black"}),
Plot.ruleX([88], {stroke: "red", opacity: 0}),
Plot.text(votes, {
Plotx: "count",
y: "vote",
text: d => d.count,
fill: "black",
// dy: -5
dx: 7
,
}).text(['59 to pass'], {
PlotframeAnchor: "top",
fontSize: 12,
x: 60, // paragraph number
lineWidth: 20,
textAnchor: "start",
lineHeight: 1.3,
// monospace: true,
// clip: true
,
}).ruleX([0])
Plot,
]// .plot({x: {label: "units →"}})
})
Code
= {
map function resetHighlight(e) {
.resetStyle(e.target);
geojson.update();
info
}
function onEachFeature(feature, layer) {
.on({
layermouseover: highlightFeature,
mouseout: resetHighlight,
click: zoomToFeature
;
})
}function highlightFeature(e) {
var layer = e.target;
.setStyle({
layerweight: 5,
color: '#666',
dashArray: '',
fillOpacity: 1
;
})
.bringToFront();
layer.update(layer.feature.properties);
info
}function zoomToFeature(e) {
.fitBounds(e.target.getBounds());
map
}const container = yield htl.html`<div style="height: 500px;">`;
const map = L.map(container).setView([50.774, -100.423], 3);
.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
Lattribution: "© <a href=https://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
.addTo(map);
})var geojson;
= L.geoJson(boundaries.geo, {
geojson style: style,
onEachFeature: onEachFeature
.addTo(map);
})
var info = L.control();
.onAdd = function (map) {
infothis._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
this.update();
return this._div;
;
}
// method that we will use to update the control based on feature properties passed
.update = function (props) {
infolet str = ''
if (props) {
let res = presDataObj[`${props.name}-${activeItem}`]
+= props.name
str if (res.for) {
+= `
str </br>
For: ${res.for} </br>
Against: ${res.against}
`
} else {
} += 'Hover over a presbytery'
str
}this._div.innerHTML = str;
;
}
.addTo(map);
info }
Code to make this all work
Map Settings and Functions
Code
function style(feature) {
return {
fillColor: getColor(feature.properties),
weight: 1,
opacity: 1,
color: 'white',
// dashArray: '3',
fillOpacity: 0.9
;
}
}function getColor(d) {
let res = presDataObj[`${d.name}-${activeItem}`]
// let res = presData.find(e => e.presbytery === d.properties.name && e.item === 'Item 1')
console.log('res', res)
if (res == null) {
return 'black'
else if (res.for === null && res.against === null) {
} return '#d3d3d3'
else if (res.for > res.against) {
} return forColor
else {
} return againstColor
}
}= '#fdc086'
forColor = '#beaed4' againstColor
Geometry Data
Code
= FileAttachment("pca.geojson").json()
pcaBounds = FileAttachment("pca_korean.geojson").json()
pcaKoreanBounds
= [
presbyteries
{name: "PCA Boundaries",
geo: pcaBounds,
value: feature => feature.properties.POP_EST,
,
}
{name: "PCA Boundaries (Korean)",
geo: pcaKoreanBounds,
value: feature => feature.properties.POP_EST,
}
]
= boundaries.geo data
Get CSV Data and Prepare Data
Code
= d3.csv("https://raw.githubusercontent.com/freestok/pca-amendments/main/presbytery_data.csv", d3.autoType)
presData
= {
presDataObj let presDataObj = {}
for (let row of presData) {
`${row.presbytery}-${row.item}`] = row
presDataObj[
}return presDataObj
}
= 59
votesForPassing
= {
votes let filteredData = presData.filter(e => e.item === activeItem)
let forVotes = filteredData.filter(e => (e.for != null && e.against != null) && e.for > e.against).length
let againstVotes = filteredData.filter(e => (e.for != null && e.against != null) && e.for <= e.against).length
return [
vote: 'for', count: forVotes},
{vote: 'against', count: againstVotes}
{
] }
Imports
Code
= require('d3')
d3 import {legend, Swatches} from "@d3/color-legend"