Size digital frames from their camera's sensor (#32) - #39
Merged
NewGraphEnvironment merged 3 commits intoAug 30, 2026
Conversation
Phases approved after plan-mode exploration against the live BC Data Catalogue. The exploration answered the issue's "unknown at filing" (14 cameras, 18 calibration files, not 2) and surfaced two findings that reshaped the work: the catalogue's SCALE is not the true image scale for digital frames, and digital sensors are not square. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GBKqedyBysV7hB4DuL98ZR
`fly_footprint()` marked every digital frame `unknown_format` with an empty geometry, which #30 made honest but did not close. Province-wide that is 223,667 of 1,670,471 frames, so the package was quietly film-only for anything after ~2010. Sensor dimensions are recoverable from the calibration reports the catalogue already links to through `camera_calibration_url`. They are parsed, never typed, into `inst/extdata/camera_formats.csv` by `data-raw/make_camera_formats.R` — 14 calibrations covering 169,688 frames, plus focal-length fallback rows for frames carrying no calibration. Two findings from the exploration reshaped the work: `SCALE` is not the true image scale for a digital frame. Measured against terrain on 40 UltraCam Eagle frames it gives 34% of true width — it is a derived nominal figure, implying a ~12.5 um pixel pitch for every camera regardless of model against real pitches of 3.9-12 um. Digital frames are sized as `pixel count x ground_sample_distance` instead, needing neither `scale` nor a DEM, and `SCALE` is no longer used for a frame fly sized itself. Sensors are not square, from 1.10:1 to 1.80:1, so a square footprint was up to 76% too deep. Non-square footprints are rotated onto the flight line via `fly_bearing()`. Film stays square and its output is unchanged. The numbers are gated on four checks before the table is written: pixel count times pitch against the stated image size, report focal against the catalogue's, plausibility bounds, and an implied ground elevation that must be a real BC elevation. The last caught a camera whose catalogue metadata contradicts its own report; it is withheld. An independent second reading of every report agreed exactly on all 14 shipped rows. `fly_georef()` excludes rotated footprints rather than double-applying its own bearing rotation, which is calibrated for axis-aligned squares. Fixes #32 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GBKqedyBysV7hB4DuL98ZR
Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GBKqedyBysV7hB4DuL98ZR
NewGraphEnvironment
deleted the
32-establish-sensor-widths-for-digital-fram
branch
August 30, 2026 17:59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
fly_footprint()marked every digital frameunknown_formatwith an empty geometry.#30 made that gap honest; it did not close it. Province-wide it is 223,667 of
1,670,471 frames (13.4%), so
flywas quietly film-only for anything after ~2010.Digital frames now get real footprints. Sensor dimensions are read from the camera
calibration reports the catalogue already links to through
camera_calibration_url,parsed into
inst/extdata/camera_formats.csvbydata-raw/make_camera_formats.R—14 calibrations covering 169,688 frames, plus focal-length fallback rows.
Two findings that reshaped the work
Both were measured during exploration and confirmed with the user before implementation.
1. The catalogue's
SCALEis not the true image scale for a digital frame. Verifiedon 40 UltraCam Eagle frames against MRDEM-30 terrain:
pixel count x GSDsensor width x (flying_height - terrain) / focalsensor width x SCALE(the old arithmetic)The first two agree to ~1% and reproduce the catalogue's own
GROUND_SAMPLE_DISTANCEto a 1.011 median ratio.
SCALEis a derived nominal figure — the pixel pitch itimplies is ~12.5 um for every camera regardless of model, against real pitches of
3.9-12 um. Shipping a sensor width while keeping the scale path would have drawn
digital footprints at a third of true size, still overlapping neighbours and still
producing coverage percentages.
SCALEis no longer used for a frameflysized itself.2. Digital sensors are not square — 1.10:1 (Leica DMC II) to 1.80:1 (Intergraph
DMC), and widths run 87.1 to 165.9 mm. A square footprint was up to 76% too deep, and a
single invented "digital" default would have been wrong by up to 90%. Non-square
footprints are rotated onto the flight line via
fly_bearing(). Film stays square andits output is unchanged.
How the numbers are trusted
They are parsed, never typed, and four checks gate the write:
px x pitchvs the report's own stated mm, both axesdmc10003972914123B is skipped where the report did not state millimetres independently, rather than
counted as a vacuous pass. F's sensitivity is stated rather than assumed: doubling every
pitch makes only 8 of 14 cameras implausible, so it is a gross-error net and B is what
gives precision.
Check E earned its keep beyond confirmation. It recovered three reports that exist only
as scanned images, and established that the focal-83 frames are a 53.9 mm
medium-format body — so the fallback that would have inferred a 104 mm UltraCam for
them is refused. That was a 93% error caught before it shipped.
Extrapolation is now opt-in per focal length with a written argument, because
extrapolation distance does not predict the error: the refused focal-83 row reached
across a 3.6% gap and was 93% wrong, while focal 120 reaches across a larger 5.5% gap
and is right.
Things caught by restoring the defect, not by reading
because
%% 180discarded exactly the sign it existed to catch.20010as2001Oand drops the micron signentirely —
Pixel Size 5.200 m, which reads as metres.Review
Three review rounds, 22 findings folded in. Round 2 found a blocker sitting inside
round 1's area: eligibility for the DEM route was keyed on the nominal-scale half-side,
which is
NAfor every camera-table row by construction — so the route the new docsname as the only route for an inferred frame was unreachable, and silently. Reviews are
in
planning/archive/.Also
width_sourcecolumn names the calibration file or fallback rule per footprintfootprint_terraingains"gsd_scaled"—nominal_scaleis documented as "sizedfrom the reported scale", which this route never does
fly_georef()excludes non-square footprints: its corner mapping is calibrated forsquare, axis-aligned ones and would apply the bearing twice (follow-up: Georeference digital frames: the corner mapping assumes a square, axis-aligned footprint #38)
fly_bearing()no longer aborts on anNAfilm_rollinst/testdata/photo_centroids_digital.gpkg— 24 real frames from two cameras0.46 apart in aspect ratio, over the DEM that already ships
CLAUDE.mdclaim that the bundled AOI has no digital frames; it has 181.That came from a
BBOX(SHAPE, ...)CQL query returning 0 for a positive control — abroken probe, not an absence
Verification
devtools::document()clean;NAMESPACEunchanged at 9 exportsHEADbaseline, all confirmed installed-vs-source artifacts for thenew internal functions
R CMD buildshipsinst/extdata; noplanning/,data-raw/,dev/Fixes #32