Skip to content
kd
29 Apr 2023
Back to blog

Visualizing roads in the cities of Canada

2 min read (209 words)

Table of contents

Let's import some packages first.

using GeoMakie
using GeoInterfaceMakie
using GeoInterface
using CairoMakie
using Shapefile
using DataFrames
using DataFramesMeta
using StringEncodings
using Pkg.Artifacts

If you want to run this interactively, you can replace CairoMakie with GLMakie, i.e.

import CairoMakie
import GLMakie

Data

Representations of Canada's national road network are available from Statistics Canada.

artifact_roadnetwork = Pkg.Artifacts.ensure_artifact_installed("roadnetwork", joinpath(@__DIR__, "Artifacts.toml"))
path = joinpath(artifact_roadnetwork, "lrnf000r21a_e.shp")
@time gdf = DataFrame(Shapefile.Table(path));
@show size(gdf)
first(gdf, 1)
35.535843 seconds (169.74 M allocations: 8.115 GiB, 23.57% gc time, 1.44% compilation time)
size(gdf) = (2242117, 26)
geometry
1Polyline(Rect(7.65014e6, 1.27149e6, 7.65038e6, 1.2717e6), Int32[0], Point[Point(7.65014e6, 1.27149e6), Point(7.65017e6, 1.2715e6), Point(7.6502e6, 1.27152e6), Point(7.65023e6, 1.27153e6), Point(7.65024e6, 1.27154e6), Point(7.65027e6, 1.27156e6), Point(7.6503e6, 1.27158e6), Point(7.65031e6, 1.27159e6), Point(7.65033e6, 1.27162e6), Point(7.65035e6, 1.27164e6), Point(7.65036e6, 1.27166e6), Point(7.65037e6, 1.27169e6), Point(7.65038e6, 1.2717e6)])\dots

The documentation says CSDNAME is the "Census subdivision name", which seems to map to cities.

Let's convert it to a proper encoding first:

latin1_to_utf8(s) = decode(Vector{UInt8}(String(coalesce(s, ""))), "Windows-1252")
@time @rtransform! gdf begin
:CSDNAME_L_UTF8 = latin1_to_utf8(:CSDNAME_L)
:CSDNAME_R_UTF8 = latin1_to_utf8(:CSDNAME_R)
end
nothing #| hide_line
17.307230 seconds (107.98 M allocations: 9.264 GiB, 31.09% gc time, 1.18% compilation time: 7% of which was recompilation)

Visualizations

We can now create a plot for each city using Makie:

CairoMakie.activate!(pt_per_unit=1.0, type = "svg")
Code
function plot_city(gdf, city_name, province = nothing)
if isnothing(province)
df = @rsubset gdf (:CSDNAME_L_UTF8 == city_name || :CSDNAME_R_UTF8 == city_name)
else
df = @rsubset gdf (( :CSDNAME_L_UTF8 == city_name || :CSDNAME_R_UTF8 == city_name ) && contains(:PRNAME_L, province))
end
# if province
# df = @rsubset df
# end
empty_theme = Theme(
fonts=(; weird="Blackchancery"),
fontsize=32,
Axis=(
backgroundcolor=:transparent,
leftspinevisible=false,
rightspinevisible=false,
bottomspinevisible=false,
topspinevisible=false,
xticklabelsvisible=false,
yticklabelsvisible=false,
xgridcolor=:transparent,
ygridcolor=:transparent,
xminorticksvisible=false,
yminorticksvisible=false,
xticksvisible=false,
yticksvisible=false,
xautolimitmargin=(0.0, 0.0),
yautolimitmargin=(0.0, 0.0),
titlefont=:weird,
),
)
with_theme(empty_theme) do
fig = Figure()
ax = Axis(fig[1, 1])
poly!.(GeoInterface.convert.(Ref(CairoMakie.GeometryBasics), df[:, :geometry]); strokewidth=0.1, strokecolor=:black, color=:black)
ax.title = city_name
fig
end
end;
plot_city(gdf, "Toronto")

plot_city(gdf, "Montréal")

plot_city(gdf, "Vancouver")

plot_city(gdf, "Ottawa")

plot_city(gdf, "Calgary")

plot_city(gdf, "Edmonton")

plot_city(gdf, "Winnipeg")

plot_city(gdf, "Victoria", "British Columbia")


Citation

@online{krishnamurthy2023visualizingroadsinthecitiesofcanada,
  author = {Dheepak Krishnamurthy},
  title = {Visualizing roads in the cities of Canada},
  year = {2023},
  date = {2023-04-29},
  url = {https://kdheepak.com/blog/visualizing-roads-in-the-cities-of-canada/},
  langid = {en},
}

For attribution, please cite this work as:

Dheepak Krishnamurthy, "Visualizing roads in the cities of Canada", April 29, 2023 https://kdheepak.com/blog/visualizing-roads-in-the-cities-of-canada/


Julia Workflow Tips and Tricks
Using Makie with Quarto