Skip to content

Add Canadian provinces and territories to the jurisdiction data - #1

Open
JonathanNus wants to merge 3 commits into
pascalorg:masterfrom
JonathanNus:canada-jurisdictions
Open

Add Canadian provinces and territories to the jurisdiction data#1
JonathanNus wants to merge 3 commits into
pascalorg:masterfrom
JonathanNus:canada-jurisdictions

Conversation

@JonathanNus

Copy link
Copy Markdown

Bones ships US-only jurisdictions, so a Canadian project either falls back to the INTL generics or, worse, gets pointed at a US state whose frost line is too shallow. The concrete failure: prairie frost penetration runs 1800–2400 mm (71–94 in), deeper than any US state in the data (max 60 in), so footings come out a foot or more short with nothing in the UI to say so.

This adds 16 entries — all 10 provinces, the 3 territories, and a generic NBC 2020 fallback.

Data only

No engine changes. Everything rides the existing path: two JSON rows merged by profileFor(), then the same five levers in applyJurisdiction().

Notes on the choices

Codes are ISO-3166-2 style (CA-ON-S, CA-BC, …) for two reasons. Bare CA is already California, so a "Canada" code there would be a genuine footgun. And jurisdictionOptions() sorts by code, so the CA- prefix groups the Canadian entries together in the dropdown without touching the sort.

Ontario is split three ways (South / East / North). Its frost line runs 1200 mm in the GTA, 1800 mm in the Ottawa Valley and 2100 mm at Thunder Bay — a wider spread than most US states carry in one row, and the Ottawa Valley is also Canada's second-highest seismic zone. One row could not represent it honestly. The other provinces are single rows.

Canada is not an IRC jurisdiction. Every row sets ircBase: null and cites NBC Division B Part 9 ("Housing and Small Buildings"), the analogue of the IRC — the same shape the Wisconsin UDC row already uses for its non-IRC state code. The note field warns inference engines not to assume IRC section numbers or IRC braced-wall-panel methods, since NBC 9.23.13 selects lateral bracing from Sa(0.2) and hourly wind pressure instead.

Unit conversions are documented per row. Canadian codes state ground snow as Ss in kPa and frost depth in mm; converted here to the psf/inch fields this schema uses, at 1 kPa = 20.885 psf.

Timezones: guess.ts gains the Canadian IANA zones, and an en-CA locale now resolves to CA-GEN rather than INTL. One caveat noted in the code: modern tzdata folds Montreal into America/Toronto, so Quebec guesses as southern Ontario. The dropdown still wins, as with every guess.

Territories carry an explicit permafrost warning in frostLineNote (so it surfaces in the panel's notes, not just the data file) — a spread-footing depth is not a meaningful output where pile or space-frame foundations are the norm.

Sources

Values are typical provincial figures, not site-specific: each row carries a caveat saying so, matching the existing disclaimer style. Quebec is flagged as still on the NBC 2015 base.

Tests

src/jurisdiction/canada.test.ts adds 10 tests. They pin the things that would regress silently — a missing data row degrades to the INTL fallback rather than throwing, so "it still renders" is not evidence a province landed. Also asserts profileFor('CA').name === 'California' so the collision stays fixed.

Full suite: 444 pass, 0 fail. tsc --noEmit clean.

One thing I found but did not fix

insulationByClimateZone is keyed "1""8", but wall-layers.ts looks it up with the raw stateClimateZone value, which reads like "5A (4A NYC/LI, 6A Adk)". Zero of the 51 existing US entries resolve, so the R-value label never renders for anyone. I left it alone — it is orthogonal to this PR and fixing it would change output for every US state. Happy to send a separate PR if useful.

@JonathanNus
JonathanNus force-pushed the canada-jurisdictions branch from 814a0c0 to 193cc97 Compare August 16, 2026 03:50
Bones ships US-only jurisdictions, so a Canadian project either falls back
to INTL generics or, worse, picks a US state whose frost line is too
shallow. Prairie frost penetration runs 1800-2400mm (71-94in) — deeper
than any US state in the data — so footings were being under-dug by a
foot or more with no warning.

Adds 16 entries covering all 10 provinces, the 3 territories, and a
generic NBC 2020 fallback. Ontario is split three ways because its frost
line runs 1200mm in the GTA to 2100mm at Thunder Bay, which is a bigger
spread than most US states carry in a single row.

Data only — no engine changes. Canada is not an IRC jurisdiction, so
every row sets `ircBase: null` and cites NBC Division B Part 9 (the
analogue of the IRC for housing and small buildings), the way the
Wisconsin UDC row already does for its non-IRC state code.

Codes use ISO-3166-2 style (CA-ON-S, CA-BC…) for two reasons: bare `CA`
is already California, and `jurisdictionOptions()` sorts by code, so the
`CA-` prefix groups the Canadian entries together in the dropdown
without touching the sort.

Also maps the Canadian IANA timezones in guess.ts, and sends an `en-CA`
locale to CA-GEN rather than INTL. Note tzdata folds Montreal into
America/Toronto, so Quebec guesses as southern Ontario — the dropdown
still wins, as it does for every guess.

Conversions are documented per row: Canadian codes state ground snow as
Ss in kPa and frost depth in mm, converted here to the psf/inch fields
this schema uses (1 kPa = 20.885 psf).
@JonathanNus
JonathanNus force-pushed the canada-jurisdictions branch from 193cc97 to 6861d07 Compare August 16, 2026 03:52
…ss engine

Two changes a host needs:

1. The wall-mode takeover ('down' while X-raying) moves from the 3D
   renderer to the panel. The renderer lives as long as the bones:framing
   node does, so leaving the Bones tab kept the host's walls hidden — and
   because wallMode is a persisted viewer preference, the stuck view
   survived reloads, and merely OPENING a scene that contained an X-ray
   node blanked the walls. The panel unmounts exactly when the user
   leaves, which is the lifetime this intent actually has. The state
   machine lives in view-takeover.ts (viewer-import-free, unit tested:
   restore on leave, never stomp a user-picked mode, no ownership when
   walls were already down). Also aligns with the plugin appearance
   contract (#625): the renderer no longer writes host presentation state.

2. index.ts exports the pure derivation surface — computeLevel,
   computeLevelUncached, computeTakeoff, cutList, the row/member types,
   extractLevels/extractRoofs — so a host estimator can compute the same
   quantities the panel shows without mounting anything.
   computeLevelUncached is exported because a whole-building rollup loop
   would otherwise thrash the 1-deep memo the panel and renderer share.
White-label commit for the PreSell deployment — intentionally NOT sent
upstream (their product name is theirs). Panel heading and badge become
"Structural Framing" / "Beta", and the 'Pascal project' fallbacks on the
printed plan set become 'Project'. Keep this as the TOP commit when
rebasing onto upstream bumps.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant