<code>
library(tidyverse)
library(VancouvR)
library(sf)
library(mountainmathHelpers)Jens von Bergmann
MountainMath
Nathan Lauster
UBC Sociology
August 18, 2026
Too much data, too little time
Mountain Doodles is an insomnia driven side project, born out of random questions, trying to give partial answers through data. During daytime at MountainMath.
How much should urban land cost? Generally we can think of the price being determined by the agglomeration effects of the city itself. That is, how easy is it to access all of the places one needs to go in the city? There’s your price.
We can break this down further for different users, but the basic principle holds. For industry, how easy is it to bring together what you need to make stuff and then move your products around afterward? For residents, in particular, we’re most interested in how easy is it to access all the jobs, though access to amenities also matter. For corporate interests, how easy is it to access your clients? For commercial enterprises, we can flip this a bit. How easy is it for all of your customers to see you’re there and access you? For a more academic review, see (Koster and Thisse 2024).
Because we’re mostly interested in agglomeration effects, we expect the city to end up with a pretty smooth price surface, where land downtown, in the centre of agglomeration effects, is most expensive, and price diminishes as one moves out from the centre. Of course, accessibility is also about transportation speed. We can also expect differences by accessibility, and think about different modes of getting around (driving, transit, walk, bike, etc.). So we’re effectively left with one chief determinant of land pricing: agglomeration, followed by two add-on considerations: accessibility, and amenity.
That effectively sets up how we should expect land price to vary across the urban landscape. But does it? Well, kind of.
There’s another chief determinant of land price of great interest to us as researchers, and that’s regulation. In particular, what’s allowed to be built on land? Mostly this is handled via zoning powers of various sorts. Hence we can draw upon zoning codes to see how this matters. What’s useful here is that observing price surfaces of land by zoning codes really allows us to see interesting cracks in the more even price surface we might otherwise expect; fault lines, if you will, illustrating how regulations operate under the surface,
This is a point highlighted recently by the excellent Yimby Country podcast, with some choice parts being re-hosted at the UCL Housing Voice Podcast. The show focused mostly on developments in New Zealand, where reforms aimed at getting a lot more housing built have successfully boosted construction and lowered rents. But guest Benno Blaschke teased that further reforms might require municipalities to “monitor land prices and land price differentials between zones”.1 There’s lots to play with in these podcast episodes, but in this post we want to focus exclusively on monitoring for differentials - cracks between zones - and how it might work.
Why would housing reforms require municipalities to look for land price differentials between zones? A large land value differential across zone boundaries is a strong indication that zoning (in the lower land value zone) is binding. That means that zoning is acting as a restriction on housing. In effect, people are being prevented from pooling their resources to share land for housing themselves as we move across the crack. Restrictions on the low land value side of the crack operate to reduce values by reducing buildable living space at the same time as they drive up values on the other side of the crack where buildable space is more available to developers. More broadly, the differentials in land values provide evidence of regional housing restriction, increasing prices and rents and forcing people to double up or otherwise keeping people out of the region. This fits into the broader concept of the zoning tax in the economics literature.
In short, the land value differential between zones gives us some measure how harmful the (lower land value) zoning is. Upzoning to the (higher land value) neighbouring zone would unlock value – removing the constraint increases housing supply, lowering prices and rents, and consequently allowing people to undouble and stay in the region.
As pointed out in the podcast, there might be some externalities to upzoning, including some good reasons why zoning boundaries are placed where they are. But how important are these reasons in relation to the value of loosening the zoning? The podcast asks:
In any of these boundaries, just what is the multiple you are willing to withstand as a polity saying, okay, well, it’s valuable enough that land prices should be able to be like 2x, one and a half x, 3x.2
How big we should allow the cracks to become is a political question.
The City of Vancouver is simultaneously a great and a terrible place to look at what lies in the cracks between zones. It’s great because we have decent data on zoning and on land value estimates at the parcel level. But it’s terrible because zoning is often not indicative of what has been built in the past, or what might get built in the future. (von Bergmann and Lauster 2023, 2024) Additionally, the highly fragmented nature of our zoning, with currently 964 different zones, creates so many boundaries that it’s almost impossible to make sense of them all. Moreover there is differentiation even within these zones, for example our now-unified low-density zone (R1-1), which allows for single family and duplexes outright, and conditionally for multiplexes, still has a host of sub-zones with separate minimum lot size and frontage regulations and separate fee schedules. Conversely, our broad Downtown (DD) zoning also includes a host of sub-schedules.
In our context, we need to explain one important deatail about how assessments in BC work. Most properties are assessed using the cost approach, which looks at sales of comparable properties to assess the value of a property, and leverages redevelopments when they happen to estimate land values. But industrial, commercial, and purpose-built rental properties are assessed differently, they assesed using the income approach. This tends to tie assessments to built floor area (and prevailing rents can cap rates) and disconnects them from zoning. When splitting the total value into land and building values the zoning, and associated potential future uses, is taken into consideration for the land value. But this is somewhat constrained by the total value of the building.
This matters in our context because very similar parcels of land having the same zoning can generate very different incomes depending on what has been built on the land. And while we might think that both should have similar land values, the income approach can lead to very differnt total and also different land values. This is something to keep in mind. Additionally, Vancouver charges an about four times higher property tax rate on commercial and industrial properties compared to rental, that directly affects land values depending on the use of a property.3
We won’t go into full details in this post, and to make things manageable we will aggregate zones into rough categories as pre-aggregated in the Vancouver Open Data. This misses a lot of nuance in the data, but we retain this in the interactive map on hover. The idea here is to show some high-level boundaries between zones to get a feel for how large the land value differential between some of these zones are. We also top-code the data to mute some outliers.
deckMap = {
const container = html`<div style="height:600px;position:relative;"></div>`;
const layer = new deck.GeoJsonLayer({
id: "relative-land-value",
data: land_value_geo,
extruded: true,
wireframe: true,
filled: true,
stroked: false,
getElevation: f => f.properties.relative_land_value,
elevationScale: 1 / 100,
getFillColor: f => hexToRgb(zoningColours[f.properties.zoning_classification] ?? "#808080"),
pickable: true,
autoHighlight: true
});
mapboxgl.accessToken = mapboxToken;
// Mapbox owns the camera/controls here; deck.gl is added as a synced
// overlay control so that zooming/rotating via the map's own controls
// (or mouse/touch) moves the deck.gl layer along with the base map.
const map = new mapboxgl.Map({
container,
style: "mapbox://styles/mapbox/dark-v10",
center: [-123.12, 49.25],
zoom: 11,
pitch: 45,
bearing: 0,
customAttribution: "Vancouver Open Data"
});
const deckOverlay = new deck.MapboxOverlay({
layers: [layer],
getTooltip: ({ object }) => object && { html: object.properties.info }
});
map.addControl(deckOverlay);
map.addControl(new mapboxgl.NavigationControl({ visualizePitch: true }), "top-left");
map.addControl(new mapboxgl.FullscreenControl({ container }), "top-left");
map.addControl(new mapboxgl.ScaleControl(), "bottom-left");
// The container is detached from the document at the moment the map is
// constructed (Observable inserts it into the DOM only after this cell
// returns), so mapboxgl measures a zero-size canvas initially. A
// ResizeObserver fires as soon as the container gets its real layout,
// triggering a resize so the map renders correctly on first load.
const resizeObserver = new ResizeObserver(() => map.resize());
resizeObserver.observe(container);
invalidation.then(() => {
resizeObserver.disconnect();
map.remove();
});
return container;
}Overall, the map does look kind of like we would expect, showing off those agglomeration effects whereby downtown land values are far and away the highest. Prices fall off as we move away from downtown. We can also see amenity and accessibility effects if we look for them. What about cracks?
One of the most striking features of the map is the way the gradient falls off sharply to the southwest of downtown, where the Green of RM-3 meets a big square block of Yellow FSD (the First Shaughnessy District neighbourhood), a highly regulated mansion intentionally full of circuitous streets that make it difficult to navigate. That’s a sharp crack, and also a very clear political choice.
Across the map, land values mostly range between $3.5k/m² and $6k/m² in lowest density zoning (R1-1, RT, and special FSD Shaughnessy zones), depending on location and lot size and sub-zones. Gradients within these zones caused by minimum lot size and frontage regulations can be observed across Blanca Street in West Point Grey, we have written in detail about this type of gradient before. (von Bergmann and Lauster 2021) Even starker effects can be seen across zoning categories, e.g. in nearby West Point Grey Village where some taller RM-3 buildings that sneak in before later downzonings show a large gradient to the neighbouring RM-3 buildings as well as the adjacent R1-1, with about double the land value. The same effect can be seen along the neighbouring commercial strip.
Remember that rental and commercial properties are assessed using the income approach, which can lead to large variations in assessed land value between properties on similar parcels even if the zoning is the same.
The sharp gradient in land price from commercial streets, which generally allow ground floor commercial with another 3 to 5 stories of housing on top, to neighbouring low density zoning can be observed throughout. Some of this might be accessibility effects driven by the arterials, especially for commercial uses. But these effects are strongest when there is resiential development above the ground floor commercial, and quite muted for single storey commercial developments. And many of the residents above those businesses would likely rather live off the arterials, where zoning prevents them from doing so, driving a separate effects we can see in the cracks.
The Comprehensive Development zones vary a lot in their land value and land value gradient, which is in the nature of these highly individualized zones that each come with their own particular rules. As they run in a line south of downtown, they follow the Skytrain’s Canada Line along the Cambie Corridor, where Transit-Oriented Development has been enabled, driving the large cracks with surrounding low-density lots.
Some gradients are purely amenity-driven, like the very high land values right along the water on Point Grey Road that drop off significantly immediately to the south. These land values are the most comparable to higher-density zones, but the exclusionary zoning keeps out all but the wealthiest of those who might enjoy waterfront living. Unlike in the West End, across the water on the downtown peninsula, there’s no beachfront land sharing allowed! In some cases, these land values get additionally boosted by unusually small lot sizes for some of these parcels, the value of the land there was recognized early on before planners and council enacted stringent subdivision rules.
It’s fun to look for zoning cracks in the land price surface! Everyone should give it a go. But there remain a few caveats to interpretation. For instance, the general problem with this approach is that the exploration for cracks only looks at relative differences across zoning boundaries. If all zoning is overly restrictive it under-estimates the overall problem. For example, if the entire city were zoned for low-density housing with some slight differentiation in what kind of housing was allowed, the differences between these zones would be small and hide the overall restrictiveness. We explored a different way to directly quantify the restrictiveness of housing in each zone in a previous post. (von Bergmann and Lauster 2025) This problem of cracks failing to get at the full measure of overall restrictiveness won’t go away until our zoning catches up to the realities of housing demand.
Relatedly, zoning has generally failed to catch the full impact of planning. Often in Vancouver, matching development to plans has still required lot by lot rezoning (and attendant costs and uncertainties). This is changing, and as we are slowly bringing back predictability by zoning for the housing we want, using zoning to measure restrictiveness becomes more useful and observing cracks provides - at least in theory - a more robust measure of actual restrictions. And looking for large land price differentials can shine a light on where zoning is most restrictive relative to what’s happening overall in the city.
As a final note, there is some similarity of this kind of analysis with that of property tax density, but property tax rates differ across uses. In particular, commercial and industrial uses paying about 4 times the residential rate, which compounds the high land values downtown to make an even starker contrast with the lightly taxed low-density residential land.
library(osmdata)
parcels <- get_cov_data("property-parcel-polygons")
subdivisions <- get_cov_data("subdivision-category-areas")
land_values <- get_cov_data("property-tax-report",
where = "tax_assessment_year = 2026",
select="land_coordinate, pid, zoning_district, zoning_classification, current_land_value")
parks <- get_cov_data("parks-polygon-representation")
cov_boundary <- zoning |> st_union() |> st_transform(4326)
golf_courses <- opq(bbox = st_bbox(cov_boundary)) |>
add_osm_feature(key = "leisure", value = "golf_course") |>
osmdata_sf()
golf_courses_cov <- golf_courses$osm_polygons |>
bind_rows(golf_courses$osm_multipolygons) |>
st_make_valid() |>
st_filter(cov_boundary) |>
select(osm_id, name, geometry)land_parcels <- parcels |>
filter(!is.na(tax_coord)) |>
summarise(across(geometry, st_union), .by=tax_coord) |>
mutate(area=st_area(geometry) |> units::set_units("m^2") |> units::drop_units()) |>
left_join(land_values |>
summarise(land_value=sum(current_land_value),
zoning_district=first(na.omit(zoning_district)),
zoning_classification=first(na.omit(zoning_classification)),
.by=land_coordinate),
by=c("tax_coord"="land_coordinate")) |>
mutate(relative_land_value=land_value/area)
plot_data <- land_parcels |>
st_collection_extract("POLYGON") |>
st_filter(parks |> st_union(), .predicate = st_disjoint) |>
st_filter(golf_courses_cov |> st_union(), .predicate = st_disjoint) |>
filter(!is.na(relative_land_value),
relative_land_value>100,
area>100,
!is.na(zoning_classification)) |>
mutate(v=log(relative_land_value)) |>
mutate(zoning_classification=case_when(
zoning_classification=="Residential Inclusive" |
zoning_district=="FSD" |
grepl("^RT-",zoning_district) ~ "Low density residential",
TRUE ~ zoning_classification)) |>
mutate(info=paste0(#tax_coord,"<br>",
"<h3>",zoning_classification,"</h3>",
"Zoning ",zoning_district,"<br>",
"Land value: ",
scales::dollar(relative_land_value),
"/m²"))As usual, the code for this post is available on GitHub for anyone to reproduce or adapt for their own purposes.
[1] "2026-08-19 17:27:30 PDT"
Local: main /Users/jens/R/mountain_doodles
Remote: main @ origin (https://github.com/mountainMath/mountain_doodles.git)
Head: [3558640] 2026-08-01: typos
R version 4.6.0 (2026-04-24)
Platform: aarch64-apple-darwin23
Running under: macOS Tahoe 26.6.2
Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/4.6/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.6/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: America/Vancouver
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
loaded via a namespace (and not attached):
[1] vctrs_0.7.3 cli_3.6.6
[3] knitr_1.51 rlang_1.2.0
[5] xfun_0.59 otel_0.2.0
[7] generics_0.1.4 S7_0.2.2
[9] jsonlite_2.0.0 glue_1.8.1
[11] git2r_0.36.2 htmltools_0.5.9
[13] mountainmathHelpers_0.1.4 scales_1.4.0
[15] rmarkdown_2.31 grid_4.6.0
[17] evaluate_1.0.5 tibble_3.3.1
[19] fastmap_1.2.0 yaml_2.3.12
[21] lifecycle_1.0.5 compiler_4.6.0
[23] dplyr_1.2.1 RColorBrewer_1.1-3
[25] htmlwidgets_1.6.4 pkgconfig_2.0.3
[27] rstudioapi_0.19.0 digest_0.6.39
[29] farver_2.1.2 R6_2.6.1
[31] tidyselect_1.2.1 pillar_1.11.1
[33] magrittr_2.0.5 tools_4.6.0
[35] gtable_0.3.6 ggplot2_4.0.3
They also point to monitoring the price differential at the urban-rural boundary, which is a much-studied sub-case of this.↩︎
We have explains how that drives mixed use redevelopment of single storey commercial properties. (von Bergmann 2016)↩︎
@misc{zoning-constraints-and-land-price-gradients.2026,
author = {{von Bergmann}, Jens and Lauster, Nathan},
title = {Zoning {Constraints} and {Land} {Price} {Gradients}},
date = {2026-08-18},
url = {https://doodles.mountainmath.ca/posts/2026-08-18-zoning-constraints-and-land-price-gradients/},
langid = {en}
}