10 |
11 |
12 | API wrapper for extracting CMHC data out of the [CMHC Housing Market Information Portal](https://www.cmhc-schl.gc.ca/hmiportal).
13 |
14 |
15 |
16 | ## Reference
17 | Documentation is [available on the GitHub pages](https://mountainmath.github.io/cmhc/).
18 |
19 | The example vignettes contain some [common use cases](https://mountainmath.github.io/cmhc/articles/basic_usage.html).
20 |
21 | ## Installation
22 | The stable version of **cmhc** can be easily installed from CRAN.
23 | ```
24 | install.packages("cmhc")
25 | ```
26 |
27 | Alternatively, the latest development version can be installed from Github.
28 | ```
29 | remotes::install_github("mountainmath/cmhc")
30 | ```
31 |
32 | ## Usage
33 | Consult the example vignette for more information. As an example, this is how to extract time series information
34 | for vacancy rate data by bedroom type for the Vancouver Census Metropolitan Area ("59933").
35 |
36 | ```
37 | library(cmhc)
38 | vacancy_data <- get_cmhc(survey="Rms",series="Vacancy Rate",dimension="Bedroom Type",
39 | breakdown="Historical Time Periods", geo_uid="59933")
40 |
41 | ```
42 |
43 | Starting with version v.0.3.2 the package has an interactive query builder helper function `select_cmhc_table()` that interactively walks through the available data and builds parameters for `get_cmhc()` like the example above. This makes it easy to discover data and build function calls to CMHC tables.
44 |
45 | ## Contributing
46 |
47 | * We encourage contributions to improve this project. The best way is through issues and pull requests.
48 | * If you want to get in touch, we are pretty good at responding via email or via twitter at [@vb_jens](https://twitter.com/vb_jens).
49 |
50 | ## Cite **cmhc**
51 |
52 | If you wish to cite cmhc:
53 |
54 | von Bergmann, J. (2025) cmhc: R package to access, retrieve, and work with CMHC data. v0.2.10. DOI: 10.32614/CRAN.package.cmhc
55 |
56 |
57 | A BibTeX entry for LaTeX users is
58 | ```
59 | @Manual{cmhc,
60 | author = {Jens {von Bergmann}},
61 | title = {cmhc: R package to access, retrieve, and work with CMHC data},
62 | year = {2025},
63 | doi = {10.32614/CRAN.package.cmhc},
64 | note = {R package version 0.2.10},
65 | url = {https://mountainmath.github.io/cmhc/},
66 | }
67 | ```
68 |
69 | ## Related packages
70 |
71 | The cmhc package is designed to work well with the [**cancensus** package](https://mountainmath.github.io/cancensus/) working with Canadian Census data the [**cansim** package](https://mountainmath.github.io/cansim/) for regular StatCan tables, and matches the census geographies via a `GeoUID` column that is shared across these packages. The [**tongfen** package](https://mountainmath.github.io/tongfen/) facilitates making geographies from different census years that CMHC reports on comparable over time.
72 |
73 | ## CMHC Attribution
74 |
75 | Subject to the [CMHC Data License Agreement](https://www.cmhc-schl.gc.ca/about-us/terms-conditions/hmip-terms-conditions), licensed products using CMHC data should employ the following acknowledgement of source:
76 |
77 |
78 | ### Acknowledgment of Source
79 |
80 | a. You shall include the following notice on all reproductions of the Information:
81 |
82 | > Source: Canada Mortgage and Housing Corporation (CMHC), name of product or information, reference date. This information is reproduced and distributed on an “as is” basis with the permission of CMHC.
83 |
84 | b. Where any Information is contained within a Value-added Product, you shall include on such Value-added Product the following notice:
85 |
86 | > Adapted from Canada Mortgage and Housing Corporation, name of product or information, reference date. This does not constitute an endorsement by Canada Mortgage and Housing Corporation of this product.
87 | or any other notice approved in advance in writing by CMHC.
88 |
89 |
90 |
--------------------------------------------------------------------------------
/R/hex_sticker.R:
--------------------------------------------------------------------------------
1 | #' Internal function to generate hex sticker
2 | #' @keywords internal
3 | generate_cmhc_hex_sticker <- function (){
4 | library(tidyverse)
5 | library(cmhc)
6 | ggplot2::theme_set(ggplot2::theme_gray())
7 | inflation <- cansim::get_cansim_vector("v41690973") |>
8 | select(Date,CPI=val_norm) |>
9 | mutate(CPIchange=CPI/lag(CPI,order_by = Date,n=12)-1)
10 |
11 | pd1 <- get_cmhc("Rms","Vacancy Rate","Bedroom Type","Historical Time Periods","48825") |>
12 | bind_rows(get_cmhc("Rms","Vacancy Rate","Bedroom Type","Historical Time Periods","48825",filter=list("season"="April"))) |>
13 | mutate(Value=Value/100)
14 | pd2 <- get_cmhc("Rms","Average Rent Change","Bedroom Type","Historical Time Periods","48825") |>
15 | bind_rows(get_cmhc("Rms","Average Rent Change","Bedroom Type","Historical Time Periods","48825",filter=list("season"="April"))) |>
16 | mutate(Value=Value/100) |>
17 | left_join(inflation,by="Date") |>
18 | mutate(Value=Value-CPIchange)
19 |
20 | pd <- bind_rows(pd1,pd2) |>
21 | filter(`Bedroom Type`=="Total") %>%
22 | left_join(filter(.,!is.na(Value)) |>
23 | select(Series,Date) |>
24 | group_by(Series) |>
25 | mutate(diff=as.integer(Date-lag(Date,order_by=Date))),
26 | by=c("Date","Series")) |>
27 | group_by(Series) |>
28 | filter(!is.na(Value) | (!is.na(lead(diff,order_by = Date)) & lead(diff,order_by = Date)>366))
29 |
30 |
31 | read_white_image <- function(path){
32 | logo<-magick::image_read(path)
33 | img <- logo[[1]]
34 | img[1,,] <- as.raw(205)
35 | img[2,,] <- as.raw(133)
36 | img[3,,] <- as.raw(63)
37 | magick::image_read(img)
38 | }
39 |
40 | library(grid)
41 | i1 <- read_white_image(here::here("images/cityscape.png")) |> rasterGrob(interpolate=TRUE)
42 | i2 <- read_white_image(here::here("images/buildings.png")) |> rasterGrob(interpolate=TRUE)
43 | i3 <- read_white_image(here::here("images/skyline.png")) |> rasterGrob(interpolate=TRUE)
44 |
45 | crs <- "+proj=lcc +lat_1=50 +lat_2=70 +lat_0=40 +lon_0=-96 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m no_defs"
46 |
47 | ca_data <- cancensus::get_census("CA21",regions=list(C="01"),geo_format='sf') %>%
48 | sf::st_transform(crs)
49 | cities <- cancensus::get_census("CA21",regions=list(CMA=c("59933","47705","35535")),geo_format='sf') |>
50 | arrange(Population) |>
51 | sf::st_transform(crs) |>
52 | select() |>
53 | sf::st_centroid() |>
54 | sf::st_coordinates() |>
55 | as_tibble()
56 | q <- ggplot2::ggplot(ca_data) +
57 | ggplot2::geom_sf(fill="#800000",size=0.01) +
58 | ggplot2::theme_void() +
59 | hexSticker::theme_transparent()
60 | bbox=sf::st_bbox(ca_data)
61 | p<-ggplot2::ggplot(pd %>% filter(!is.na(Value)),ggplot2::aes(x=Date,y=Value,color=Series)) +
62 | ggplot2::geom_point(aes(shape=Quality),size=0.8) +
63 | ggplot2::geom_line(size=0.3) +
64 | #ggplot2::scale_colour_brewer(palette="Dark2",guide="none") +
65 | ggplot2::scale_color_manual(values=c("Average Rent Change"="#f0c60a",
66 | "Vacancy Rate"="#f0430a"),
67 | guide="none") +
68 | ggplot2::scale_shape_discrete(guide="none") +
69 | ggplot2::labs(x="",y="") +
70 | ggplot2::theme_void() +
71 | hexSticker::theme_transparent()
72 |
73 | xmin <- as.numeric(bbox$xmin)
74 | xwidth <- as.numeric(bbox$xmax-bbox$xmin)
75 | ymin <- as.numeric(bbox$ymin)
76 | ywidth <- as.numeric(bbox$ymax-bbox$ymin)
77 |
78 | size=0.1
79 |
80 | pp <- q +
81 | ggplot2::annotation_custom(i3, xmin=cities$X[1]-size*xwidth,xmax=cities$X[1]+size*xwidth,
82 | ymin=cities$Y[1],ymax=cities$Y[1]+2*size*ywidth) +
83 | ggplot2::annotation_custom(i1, xmin=cities$X[2]-size*xwidth,xmax=cities$X[2]+size*xwidth,
84 | ymin=cities$Y[2],ymax=cities$Y[2]+2*size*ywidth) +
85 | ggplot2::annotation_custom(i2, xmin=cities$X[3]-size*xwidth,xmax=cities$X[3]+size*xwidth,
86 | ymin=cities$Y[3],ymax=cities$Y[3]+2*size*ywidth) +
87 | ggplot2::annotation_custom(ggplot2::ggplotGrob(p),
88 | xmin=1.5*bbox$xmin,xmax=1.5*bbox$xmax,
89 | ymin=bbox$ymin*0.8+bbox$ymax*0.2,ymax=bbox$ymax*1.1) +
90 | hexSticker::theme_transparent()
91 |
92 | hexSticker::sticker(pp, package="cmhc",
93 | p_size=12, p_y=1.5,
94 | s_x=1, s_y=0.78, s_width=1.5, s_height=1.5,
95 | h_color="#FF0000",
96 | h_fill="grey40",
97 | p_color="white",
98 | filename=here::here("logo.svg"))
99 |
100 | hexSticker::sticker(pp, package="cmhc",
101 | p_size=22, p_y=1.65,
102 | s_x=1, s_y=0.78, s_width=1.5, s_height=1.5,
103 | h_color="#FF0000",
104 | h_fill="grey40",
105 | p_color="white",
106 | filename=here::here("logo.png"))
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/docs/bootstrap-toc.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/)
3 | * Copyright 2015 Aidan Feldman
4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */
5 | (function() {
6 | 'use strict';
7 |
8 | window.Toc = {
9 | helpers: {
10 | // return all matching elements in the set, or their descendants
11 | findOrFilter: function($el, selector) {
12 | // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/
13 | // http://stackoverflow.com/a/12731439/358804
14 | var $descendants = $el.find(selector);
15 | return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])');
16 | },
17 |
18 | generateUniqueIdBase: function(el) {
19 | var text = $(el).text();
20 | var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-');
21 | return anchor || el.tagName.toLowerCase();
22 | },
23 |
24 | generateUniqueId: function(el) {
25 | var anchorBase = this.generateUniqueIdBase(el);
26 | for (var i = 0; ; i++) {
27 | var anchor = anchorBase;
28 | if (i > 0) {
29 | // add suffix
30 | anchor += '-' + i;
31 | }
32 | // check if ID already exists
33 | if (!document.getElementById(anchor)) {
34 | return anchor;
35 | }
36 | }
37 | },
38 |
39 | generateAnchor: function(el) {
40 | if (el.id) {
41 | return el.id;
42 | } else {
43 | var anchor = this.generateUniqueId(el);
44 | el.id = anchor;
45 | return anchor;
46 | }
47 | },
48 |
49 | createNavList: function() {
50 | return $('');
51 | },
52 |
53 | createChildNavList: function($parent) {
54 | var $childList = this.createNavList();
55 | $parent.append($childList);
56 | return $childList;
57 | },
58 |
59 | generateNavEl: function(anchor, text) {
60 | var $a = $('');
61 | $a.attr('href', '#' + anchor);
62 | $a.text(text);
63 | var $li = $('YEAR: 2019 63 | COPYRIGHT HOLDER: Jens von Bergmann 64 |65 | 66 |
Copyright (c) 2019 Jens von Bergmann
65 |Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
66 |The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
67 |THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
68 |R/helpers.R
61 | cmhc_ct_translation_data.RdA dataset with geographic identifiers for CMHC and Census at the CT level
66 |A tibble with Census and CMHC geographic identifiers
72 |Custom data extract from CMHC
76 |R/helpers.R
61 | cmhc_cma_translation_data.RdA dataset with geographic identifiers for CMHC and Census at the CMA level
66 |A tibble with Census and CMHC geographic identifiers
72 |Custom data extract from CMHC
76 |R/helpers.R
61 | cmhc_csd_translation_data.RdA dataset with geographic identifiers for CMHC and Census at the CSD level
66 |A tibble with Census and CMHC geographic identifiers
72 |Custom data extract from CMHC
76 |R/helpers.R
61 | cmhc_csd_translation_data_2023.RdA dataset with geographic identifiers for CMHC and Census at the CSD level for 2023 data portal version
66 |A tibble with Census CSD and CMHC METID geographic identifiers
72 |Custom data extract from CMHC
76 |R/user_settings.R
61 | show_cmhc_cache_path.RdView saved cache path
66 |show_cmhc_cache_path()a character string with the CMHC cache path
77 |show_cmhc_cache_path()
82 | #> [1] "/Users/jens/data/cmhc.data"
83 |
84 | Data obtained via this package will automatically translate internal CMHC quality labels 68 | using this translation vector, this named vector is useful when working with CMHC data obtained from other 69 | sources like sporadic excel sheets or data scraped from PDF reports.
70 |cmhc_quality_labelsA named vector to translate internal CMHC quality indicators to plain text.
79 |Interactive table selector
66 |select_cmhc_table()A string containing the function call to access the selected table
77 |if (FALSE) {
82 | select_cmhc_table()
83 | }
84 | List available CMHC surveys
66 |list_cmhc_surveys()A data frame with available survey names.
77 |list_cmhc_surveys()
82 | #> # A tibble: 6 × 1
83 | #> Survey
84 | #> <chr>
85 | #> 1 Scss
86 | #> 2 Rms
87 | #> 3 Srms
88 | #> 4 Seniors
89 | #> 5 Census
90 | #> 6 Core Housing Need
91 |
92 | List available CMHC series
66 |list_cmhc_series(survey = NULL)Optional survey to filter by
A data frame with survey names, and available series names.
83 |list_cmhc_series("Rms")
88 | #> # A tibble: 7 × 2
89 | #> Survey Series
90 | #> <chr> <chr>
91 | #> 1 Rms Vacancy Rate
92 | #> 2 Rms Availability Rate
93 | #> 3 Rms Average Rent
94 | #> 4 Rms Average Rent Change
95 | #> 5 Rms Median Rent
96 | #> 6 Rms Rental Universe
97 | #> 7 Rms Summary Statistics
98 |
99 | List available CMHC dimensions
66 |list_cmhc_dimensions(survey = NULL, series = NULL)Optional survey to filter by
Optional series to filter by
A data frame with survey names, series names, and available dimension names.
87 |list_cmhc_dimensions("Rms","Vacancy Rate")
92 | #> # A tibble: 5 × 3
93 | #> Survey Series Dimension
94 | #> <chr> <chr> <chr>
95 | #> 1 Rms Vacancy Rate Bedroom Type
96 | #> 2 Rms Vacancy Rate Year of Construction
97 | #> 3 Rms Vacancy Rate Structure Size
98 | #> 4 Rms Vacancy Rate Rent Ranges
99 | #> 5 Rms Vacancy Rate Rent Quartiles
100 |
101 | List available CMHC filters
66 |list_cmhc_filters(
70 | survey = NULL,
71 | series = NULL,
72 | dimension = NULL,
73 | breakdown = NULL
74 | )Optional survey to filter by
Optional series to filter by
Optional dimension to filter by
Optional breakdown to filter by
A data frame with available filters
100 |list_cmhc_filters("Rms","Vacancy Rate","Bedroom Type","Historical Time Periods")
105 | #> # A tibble: 1 × 5
106 | #> Survey Series Dimension Breakdown Filters
107 | #> <chr> <chr> <chr> <chr> <list>
108 | #> 1 Rms Vacancy Rate Bedroom Type Historical Time Periods <named list [2]>
109 |
110 | R/user_settings.R
63 | set_cmhc_cache_path.RdThe cmhc package provides access to custom cmhc geographies, these are large files and should be 68 | stored in a permanent location. This function sets the CMHC_CACHE_PATH environment variable and optionally 69 | installs it in the .Renviron file for future use. This is only needed when using the `get_cmhc_geography()` function.
70 |set_cmhc_cache_path(cache_path, overwrite = FALSE, install = FALSE)a local directory to use for saving cached data
Option to overwrite any existing cache path already stored locally.
Option to install permanently for use across sessions.
a character string with the CMHC cache path
95 |if (FALSE) {
100 | # This sets the cache path for the duration of the current session
101 | set_cmhc_cache_path("~/cmhc_cache")
102 |
103 | # This will set the cache path permanently until overwritten again
104 | set_cmhc_cache_path("~/cmhc_cache", install = TRUE)
105 | }
106 |
107 |