Federal, provincial and municipal results on one map of the City of Vancouver — poll by poll, voting area by voting area — with the 2021 census beside them.
Everything is read from inside this file. Nothing is uploaded, and apart from the optional street basemap nothing is fetched — the analysis runs in this tab, on this machine. The Data tab says where each dataset came from and lets you replace it.
Computed from the data loaded here, by the tabs named beside each one, and recomputed when you change a setting. Each carries how it was arrived at: a counted figure is one an agency reported for these areas, a modelled one was moved onto a geography its source does not use.
Each of these is worked through on the How to read this tab, with the measurements behind it rather than the assertion alone.
Provincial boundaries are not bundled here because Elections BC publishes them under their own licence. Open the Data tab and load a voting-area file to draw the overlay — a zipped shapefile, KML, KMZ or GeoJSON all work, in BC Albers or lon/lat.
Click anywhere on the map to read the federal polling division, the provincial voting area and the dissemination area covering that point.
Street basemap © OpenStreetMap contributors, © CARTO — loaded from the web; everything else in this file works offline. Boundary coordinates are rounded to five decimals. Federal source: Elections Canada, Polling Division Boundaries 2025 (Metro Vancouver KMZ). UBC and UEL sit in Electoral Area A, outside the City of Vancouver.
Get these from Elections BC's
GIS spatial data
page or the BC Data Catalogue,
and load the voting-area file for the election you are comparing against.
For 2024 that is Provincial Electoral District Voting Areas – Gazetted 09/19/2024
(EBC_VOTING_AREAS_BS11_POLY_SVW), ordered from the catalogue as GeoJSON;
the order’s .zip loads here as delivered, all 5,778 areas of the province,
and is clipped to the study area while it is read. Districts are identified by
ED_ABBREVIATION and areas by VA_CODE. The dataset is
areal only — every one of the 5,778 records is VA_TYPE: Areal
with a polygon — so voting at a care home or hospital has no geography here;
those ballots arrive as the district-level Special voting channel, 1.35% of
the city’s 2024 vote, and are spread across their district.
A zipped shapefile (.shp + .dbf + .prj),
.kml, .kmz or .geojson all work.
BC Albers, UTM zone 10N and lon/lat are converted automatically.
Nothing is uploaded anywhere — the file is read in this browser tab.
To keep a small file for repeated use, python3 tools/clip_geojson.py
writes just the study area (see the README).
Elections Canada publishes one pollresults_resultatsbureau<riding>.csv
per electoral district. Load them one at a time or as a .zip;
each file adds to what is already loaded.
Elections BC publishes results by voting area for each electoral district. Long files (one row per candidate) and wide files (one column per party) are both read.
For 2024 there is a catch. It was the first general election where a voter could use any voting place, so Elections BC reports the count by voting place — a point — rather than by voting area. A file with longitude and latitude columns is read that way automatically: every voting area is given to the nearest final-voting place of its own electoral district, and that place’s ballots are split across the areas it serves in proportion to population, or to ground area when no census is loaded. Advance voting, the district office, vote by mail, special and assisted telephone voting and out-of-district ballots have no place of their own and are spread across the whole district instead. These catchments are modelled here, not published by Elections BC. The status line below says how much of the count took which route, and the How to read this tab says what that costs a correlation.
Elections BC reports turnout as the share of registered voters who voted, and publishes that denominator per electoral district in the Statement of Votes rather than in the results file. Load a two-column table — district, registered voters — and the Results tab reports turnout per district and city-wide. Any column named for the voters who voted is ignored: that is the numerator, and the atlas already has it.
Dissemination areas are the third geography: election results are moved onto them
through the crosswalk and correlated with census variables on the Neighbourhood profile
tab. Dissemination blocks (optional) weight every crosswalk by where people live
rather than by area. The national downloads are large, so run
python3 tools/filter_census.py once (see the README) and load its
outputs here; the national files themselves also load, more slowly.
Adapted from Statistics Canada, Census Profile, 2021 Census of Population, and the
2021 Geographic Attribute File; this does not constitute an endorsement by
Statistics Canada.
Two files from the City of Vancouver open data portal: the results archive and the voting places for the same year, joined on the numeric voting place id. 2026 and 2022 both read. Load either first — the results appear once both are in.
This election is not like the other two. You may vote at any voting place in Vancouver, and most people do: in 2026, 83 of the 85 election-day places were vote-anywhere centres and 59 more were advance places drawing from across the city, together holding 90% of the ballots. So the ballots are smoothed onto areas by distance rather than assigned to the nearest place, and there is no municipal turnout by area anywhere in this atlas — measured against the federal turnout surface, any such map agrees at about r 0.2 however it is built. The party share survives that and is offered; the city’s own published city-wide rate is on the Results tab. “How to read this” has the numbers.
Load another year’s results and voting places — the 2022 pair, say — and the map can show the swing: how much a party’s share rose or fell in each area between the two elections, for the race chosen above. Only parties on the ballot both times under the same name are offered.
This one is outside Advanced on purpose: it is the only section that can never be built into the file, so on the day a roll arrives this is the thing to load, not a replacement for something already here. Any list of locations, counted onto every layer that is loaded. Rows can carry their own coordinates — longitude and latitude columns, a GeoJSON geometry column, or one column holding both numbers — or they can carry a civic number and a street, in which case they are joined against the address lookup table below. Nothing is uploaded: like every other file here, it is read in this browser tab and stays on this machine.
An elector roll is personal data. The atlas only ever writes counts per area, never records, and the report says how many areas hold so few that the count describes the people in it.
One row per address with how many are there, plus the areas it falls in. No names and no identifiers — a mail drop needs the door and the quantity. It is still derived from a roll, so it says which buildings hold electors and how many.
Rank this list on
An estimate, not a lookup, and reported as one. It keeps to the same side of the street and gives up beyond two blocks — a street is often the boundary between two areas.
Only needed when the file above carries addresses rather than coordinates, and only once: the City of Vancouver property addresses turn a civic number and a street into a point. This is not where the roll goes.
What was heard at the door, joined to addresses the same way the roll is. It stays in this tab: only the score you assign below and a contact count are kept, and nothing from the file — no name, no note, no number — reaches the export or the built page. There is deliberately no way to bake canvass data into a shared build.
What each answer is worth, from 0 to 1. Words this recognised are filled in; numbers are left blank on purpose — some databases count 1 as strongest support and others count it as strongest opposition, and guessing wrong would invert the whole canvass without anything downstream being able to tell. Leave a value blank to keep it out of the score entirely, which is what a refusal or an unanswered door should usually be.
Counts as reported, before anything is joined to a boundary, moved across the crosswalk or spread from a voting place. Every other tab models something; this one is the place to check a headline figure against the file it came from.
Votes and share of the valid vote, across every district in the file.
Ballots by voting opportunity. Only the final-day places have a catchment on the map; everything else is spread across a whole district, so this is the share of the count that carries real location.
Click a heading to sort. Margin is the gap between first and second, in points of the valid vote.
Federal polling divisions and provincial voting areas are drawn by different agencies and share no boundaries, so results cannot be joined on a key. This measures how much of each federal division falls inside each voting area and splits votes in those proportions. Read the How to read this tab before quoting any number from here.
Turnout is ballots cast (valid plus rejected) over electors, for each area, in each election. The aggregate combines the two with the weight below. Advance polls and special ballots have no boundary; by default they are left out, which understates turnout in areas where people vote early. The provincial side has no electorate per area at all — Elections BC publishes registered voters per district only — so its last three columns divide the ballots by two counts that are available per area, federal electors and census residents aged 15 and over. Neither is turnout, and the spread between them is shown because it is the size of that choice. Read the How to read this tab before quoting a figure.
Click a column heading to sort. Tick areas, or shift-click them on the map, to pool them in the basket.
Federal (2025) and provincial (2024) results are moved onto Statistics Canada's 2021 dissemination areas through the crosswalk, so every area carries turnout and party shares next to its census profile. A correlation here is a statement about neighbourhoods, not about people — read the How to read this tab before quoting one.
Tick the variables to compare; add any of the census characteristics by name. Click a column heading to sort, or any row to plot it below.
Names here are the plain ones — hover any variable for the statistical definition behind it, which is also what the exported column keys
(pct_renter, median_hh_income) refer to.
Electors on a roll, minus ballots cast, area by area. Load a roll under A file of places on the Data tab and it is counted onto every layer here. Nothing on this tab is a count of people unless both halves of the subtraction were counted — the route of each is named beside every figure.
Areas ranked from the number who did not vote and the party share in each. The rank orders areas against one another — it is not a count of votes available, which would be a claim about how particular people would vote, and nothing here measures that.
One row per area, with the route of each half in its own column so a reader can keep counted and modelled figures apart.
Federal polling divisions and provincial voting areas are drawn independently and share almost no boundaries. There is no key to join them on, so this atlas estimates the overlap directly: it lays a lattice of equally spaced points over the area, records which federal division and which voting area each point falls in, and treats the counts as areas. The share of a federal division lying inside a given voting area becomes the fraction of that division's votes assigned to it.
Elections Canada marks mobile polls (type M, numbered from 500) and single-building polls (type S, numbered from 400). Mobile polls serve care homes and similar institutions; their polygons are token markers a few metres across, not catchments. They are excluded from the map by default. Because the lattice can miss polygons that small, any polygon the lattice barely sees is instead assigned whole to the unit containing an interior point of it, so its votes are never silently lost.
Advance polls, special ballots and mail-in votes have no boundary in either agency's geography. They are a large and growing share of the vote, and they are not distributed like election-day votes. Whenever results are loaded, the Data tab reports what fraction of the votes in the file landed in a mapped poll. Treat any correlation here as a statement about election-day ordinary votes, and quote that coverage figure alongside it.
Both the unweighted and the vote-weighted Pearson coefficient are shown. They differ because small polls are numerous and noisy; the weighted figure is usually the more honest summary of how the electorate actually behaved, while the unweighted one describes how the polls behave as units. Spearman's rank coefficient is given as a check against a handful of outliers driving the fit. Note that units are not independent observations — neighbouring polls resemble each other — so the confidence interval is narrower than the spatial autocorrelation really warrants.
Redistributing onto provincial voting areas moves federal votes and leaves provincial votes untouched; onto federal divisions it does the reverse. Overlap pieces move both and give the most units, but each is smaller and noisier. If a result only holds on one of the three, it is a property of the method rather than of the electorate.
Forty-three per cent of the 2025 federal ballots in Vancouver were cast at an advance poll, and an advance poll has no boundary of its own. Spreading its ballots across the whole riding — about 190 polling divisions — was the crudest thing this atlas did, and it applied to nearly half the vote.
Elections Canada publishes the answer. Its polling-division file records, for every ordinary division, the advance poll that division reported to. Each advance poll serves three to fourteen divisions, ten on average, so its ballots now go to those and to nothing else. The share of federal ballots sitting either on a division or in a named set of about ten rises from 44% to 84%.
This is published, not invented. It is worth being clear about the contrast with the provincial side of the same map: the voting-place catchments described above are this atlas’s own guess at a boundary Elections BC drew and did not publish, and every caveat there is real. None of it applies here. The set of divisions behind a federal advance poll is a fact Elections Canada states.
Within a served set the ballots are split in proportion to electors, not equally. The divisions of one advance poll are roughly the same size but not exactly: across Vancouver the largest is typically 1.5 times the smallest and in one case six times, so an even split would be wrong by about 13% in a typical division. The assumption that remains is that people in those ten divisions chose to vote early at similar rates — a far smaller claim than the same assumption across a whole riding, but still a claim.
Two consequences worth naming. A division whose advance poll is not in the file keeps the district-wide spread rather than losing its ballots. And an advance poll whose divisions all lie outside the study area gives that area nothing — which is how the Burnaby end of Vancouver Fraserview—South Burnaby now settles itself, with no share to estimate and no early-voting rate to guess at. The elector-share scaling described below is still used, but only for what is left: special ballots and mail.
Two of the six federal ridings cross the City of Vancouver line. Vancouver Quadra reaches into UBC and the University Endowment Lands; Vancouver Fraserview—South Burnaby is a third Burnaby by electors. Polls outside the city are tagged in the boundary file, and the Area control on the Map tab decides what to do with them — but excluding a poll is only half of the problem.
A straddling riding cast its advance and special ballots across the whole riding, and those ballots have no polygon at all. Apportioning them onto the polls that remain would hand Burnaby’s share to Vancouver: on the 2025 files that is roughly 9,700 ballots landing on a part of the city that did not cast them. So each district’s pool is scaled by the share of its electorate inside the study area — measured from the loaded file, not assumed — and only that share is spread, onto the polls inside. Every riding wholly inside has a share of 1 and nothing about it changes.
Two smaller things follow from the same distinction. An ordinary poll with no boundary is not the same as an advance poll with no boundary, even though a join cannot tell them apart: the first was cast somewhere this atlas does not cover, and is reported rather than spread. And the study area is the Area control alone — hiding mobile polls changes what is drawn, not where the city is, so no apportioned figure moves when that switch does.
The remaining assumption is that people inside and outside the line vote early at the same rate. There is no way to test it from published data: Elections Canada reports an advance poll by the poll, not by the elector’s home division. The Results tab therefore keeps reporting the file exactly as loaded, and names how many of its ballots were cast outside the chosen area.
Turnout is ballots cast — valid votes plus rejected ballots — divided by the electors on the list for that poll or voting area. Electors, votes and rejected ballots are all counts, so they move through the crosswalk together, and turnout on a common geography is simply the electors-weighted mean of the polls that feed it.
British Columbia's 2024 general election was the first where a voter could use any voting place, so Elections BC reports the count by place rather than by voting area. The voting areas still exist — every address belongs to one, and Elections BC assigns each of them to a place — but that assignment is not in the results file, and the count that comes back is attached to a point.
To put those numbers on a map at all, this atlas models the assignment: every voting area goes to the nearest final-voting place of its own electoral district, and that place's ballots are divided among the areas it serves in proportion to population, or to ground area when no census is loaded. Ballots cast anywhere else — advance voting, the district electoral office, vote by mail, special and assisted telephone voting, and out-of-district ballots — have no location that means anything, and are spread across the whole district instead. Nothing is dropped: every ballot in the file lands on some area, and the Data tab says what fraction took which route.
Three things follow, and they matter more than the map looks like they do. Within a catchment, the variation you see is the census, not the election. Every area served by one place carries that place's shares, scaled by its population; differences between neighbouring areas of the same catchment are an artefact of the population weighting. A catchment is a guess at a boundary Elections BC actually drew. Nearest-place is a reasonable guess in a dense city and a worse one where a district is long or a hall sits at one end. And spreading one place's result over five areas does not make five observations. The Compare and Neighbourhood profile tabs therefore report the number of original reporting units beside the number of areas charted, and compute the confidence interval on those reporting units; quote a coefficient with that figure, not with the count of polygons.
Provincial turnout is a separate matter, and a question of which denominator exists at which geography. Elections BC defines turnout as the share of registered voters who voted, and publishes that count per electoral district in the Statement of Votes; the results file itself carries none. Given that table, the Results tab reports turnout per district and city-wide, and those are measurements, not estimates.
Per-area provincial turnout stops there deliberately. One registered-voter count per district, split across the district's voting areas, would be a model dressed as a measurement — and since the only sensible split is by population, the variation between neighbouring areas would be the census rather than the electorate. So the provincial turnout column stays blank per area, the combined ranking falls back to the federal side alone and says it is partial, and party shares and ballot counts are unaffected. An elector count by voting area would turn it on for real; so would Elections BC's own voting-area-to-place assignment, which would replace the catchment model entirely.
Vancouver's municipal election is measured differently from the other two in this atlas, and the difference is not a detail. Federally and provincially you are assigned a voting place near your home, so the ballots counted there were mostly cast by the people who live around it — which is what makes a catchment a reasonable guess. Municipally you may vote at any voting place in the city. In 2022, 68 of the 104 places were vote-anywhere centres carrying 54.8% of the ballots, and 22 more were advance places carrying another 40.1%. Only 12 ordinary neighbourhood places remained, and they took 5.1% of the vote.
2026 went further in the same direction. 83 of the 85 election-day places were vote-anywhere centres, carrying 40.9% of the ballots; 59 advance places in the city carried 49.4%, more than election day did; the two ordinary neighbourhood places took 0.3%. Mail ballots were 8.2% of the count and are reported as one city-wide row with no place, so they are kept off the map rather than put somewhere plausible. The measurements below were made on 2022 and have not been re-run on 2026; with even less of the vote cast near home, there is no reason to expect a municipal turnout surface to have become more trustworthy.
So a nearest-place catchment is the wrong instrument here, and it fails visibly. Applied
to the 2022 results it put more ballots into 32 polling divisions than those divisions
have electors — one advance place at Dunbar dropped 4,166 ballots onto four
divisions of about 450 electors each. What this atlas does instead is spread each
place's ballots over every area by distance, exp(−distance / bandwidth),
with a different bandwidth for each channel: about 600 m for a final-day place and
about 2 km for an advance one, because the advance network is sparse and drew from
further away. That removes all 32 impossible areas and conserves every ballot exactly.
Both bandwidths are on the Data tab, so the sensitivity is yours to check rather than
ours to assert.
Two things were tried and rejected, and both are worth stating because they are the obvious things to try. Constraining each area's total to its electors times the city-wide rate — the natural way to stop any area exceeding its own electorate — returns that rate in every area: standard deviation 0.0 points, zero correlation with anything. It does not estimate turnout, it asserts it. The atlas therefore uses a ceiling at an area's own electorate, which binds only where the model is impossible and is never a target. And a single bandwidth for every place, rather than one per channel, is worse than doing nothing at all.
That leaves the question of whether the smoothed surface is worth anything. Measured against the federal 2025 turnout surface — ordinary ballots on their own division, advance ballots on the divisions their advance poll actually served, which the municipal model never sees — every version tried agrees at about r 0.2, and aggregating to 1 km and 2 km cells does not rescue it (0.23 and 0.21). That is about 4% of the variation. Two real elections at one geography would normally agree far more closely than that. So this atlas offers no municipal turnout by area, at all. A map of where municipal ballots were cast is largely a map of where the voting places were.
Party share is a different matter, and it is why the municipal layer exists. A share is a ratio of two numbers measured at the same place, so it needs no denominator tied to the area it is drawn on: travel blurs it, but does not bias it the way it biases a rate. Measured on the 2022 mayoral results, a voting place's share agrees with its neighbours within a kilometre at r 0.76. The surface that survives is offered; the one that does not is absent.
The one turnout figure this election does contribute is the city's own: 208,514 ballots on 474,125 registered voters in the City of Vancouver in 2026, 44.0% (170,274 on 464,126 in 2022). That has no geographic modelling in it whatever, and the city publishes it; the Data tab reads it from whichever archive is loaded. The city's headline 43.39% divides by a total that includes the UBC Lands and the UEL. The places there — three in 2026, counting the advance place at the Nest — are excluded throughout — their electors vote for School Trustee and nothing else, and the city counts them separately too. And in a ten-seat council race a ballot carries up to ten votes, so the party columns are votes and the ballots column is ballots; the atlas keeps them apart rather than reporting nine times the turnout.
With an earlier election loaded, the map can show a party's swing: its share in the mapped election minus its share in the earlier one, for the same race. Each year is smoothed from its own voting places with the same bandwidths, and only then are the two shares subtracted, so the swing inherits everything said above about municipal shares: it is blurred by travel, but a share is not biased by it the way a rate is.
Two cautions are specific to the swing. The voting places moved between elections — 104 in 2022, 147 in 2026 — so part of an area's swing is a change in where its neighbours could vote. And when a party moved the same way everywhere, a map of raw swing is one colour; the version measured against the city-wide swing is the one that shows where it held up. The city-wide swing itself is computed from every ballot, mail included, and involves no smoothing at all. Only parties on the ballot both times, under the same name, are offered; the questions are never compared, because they ask about different things each time.
What can be done instead is to divide the ballots by something that is available per area, and to be exact about what that something is. Two counts qualify, and the Turnout tab reports both, side by side, in its last three columns — never called turnout, and never folded into the aggregate:
The Spread column is the difference between the two in percentage points, and it is the reason for showing both: it is the size of the choice of denominator, per area, instead of an argument about which one to trust. Where an area comes out above 100% the tab says how many did — that is a fact about the denominator not fitting that area, not about the ballots. Either can shade the map, and the legend names the denominator every time. The only thing that would make any of this turnout is a registered-voter count by voting area; if one is ever published it loads in section 3 of the Data tab, and the real column fills in beside these two.
The Non-voters tab does one subtraction — electors on a roll, minus ballots cast, area by area — and almost everything on it exists because that subtraction is easy to get plausibly wrong.
The two halves do not arrive the same way. A roll loaded as a file of addresses is counted onto every layer by point-in-polygon: a genuine count on a geography the roll was never published for, and the only elector count in this atlas of which that is true. Federal electors on a provincial area are areally interpolated instead. Municipal ballots on any area are a kernel's output rather than a count of anything that happened inside it. So each half records how it got there, a subtraction inherits the weaker of the two, and only counted minus counted is called a count anywhere on the tab. The badge beside the figures, the second line of every column header, the map legend and the export columns all carry it.
A negative is a result, not an error, and is never clamped. More ballots than roll electors means the roll does not describe the people who voted there — a different vintage, a different franchise, a boundary that moved. It is the best diagnostic the tab produces, and a clamped negative would be a denominator error hidden rather than found. For the same reason the municipal smoothing is capped at each area's own federal electorate and never at a municipal roll: capping it there would floor every municipal gap at zero and destroy the diagnostic. Areas sitting at that ceiling are flagged, since a zero in one of them is arithmetic rather than a finding.
An area with no roll entry is not an area with nobody on the roll. The divisions covering UBC and the University Endowment Lands are the expected case: those electors are on Vancouver's roll for the school trustee ballot, and the city's property file does not address land outside the city. They come out with no entry, are counted separately and are left out of every total, rather than appearing as places where the whole electorate stayed home.
Census residents are not a roll, and cannot be picked as one. Subtracting ballots from residents aged 15 and over would put 15-, 16- and 17-year-olds into a count of people who did not vote. That count stays where it belongs, on the Turnout tab, labelled “Per resident 15+”.
The mail ranking is ordinal, and is not a count of votes. Areas are ranked from the number who did not vote and a party share together. Mail goes to a geography rather than to a person picked out by an inferred belief, so the unit the ranking infers from and the unit it acts on are the same one, which is what an area-level score can honestly support. What it cannot support is “N votes available”: that is a claim about how particular people would vote, and nothing here measures it. The weighting between the two ingredients is a control with its default printed, since persuading and mobilising trade them differently and there is no principled split.
Nothing from a roll is written into this file. It is read in this tab, on this machine; what is kept afterwards is a count per address and a count per area, never a record per person. The tool that bakes datasets into a build has deliberately no flag for a roll or for canvass data, and a test asserts that it never will.
Statistics Canada's dissemination areas are the smallest geography with a full census profile: 400 to 700 people each, drawn to follow streets, so they are neighbourhood-sized and overlap polls the way voting areas do. Election results reach them through the same crosswalk, so each carries an estimated turnout and party share beside its census variables.
Which geography to correlate on. Either side of the crosswalk can be the unit, and the choice decides what is modelled and what is measured. On dissemination areas the census is exact and both elections are estimates; for the 2024 provincial results that is two estimates in a row, since those were modelled from voting places onto voting areas first. On provincial voting areas the provincial results are one step closer to as-reported and the census is the estimate instead: a count is shared out across the overlaps, while a rate, a percentage or a median is averaged over them weighted by population, because those do not add. Neither is the right answer in general. Agreement between the two is worth more than either alone, and a coefficient that appears on one geography and vanishes on the other was probably the interpolation talking.
The map is drawn with Leaflet. Street tiles come from CARTO (Positron in the light theme, Dark Matter in the dark) or from OpenStreetMap's standard style, all built on OpenStreetMap data, and are requested from the provider only while the page is open. Each tile request is an ordinary web request to the provider's servers, so it carries your IP address, browser identification and request headers as well as the tile coordinates, which reveal the area and zoom level on screen; the provider's privacy policy governs that data. Nothing else is sent: boundary and results files stay in the browser tab. If the tiles do not load the map says so and keeps working; choose “None” under Basemap to make no requests at all and work fully offline. The attribution in the map's corner is a condition of the providers' terms.