Skip to content

fix(GeoMap): one continuous heightfield so patches never show cliffs or holes - #14860

Merged
DonLakeFlyer merged 1 commit into
mavlink:masterfrom
DonLakeFlyer:geomap-heightfield-drape
Aug 13, 2026
Merged

fix(GeoMap): one continuous heightfield so patches never show cliffs or holes#14860
DonLakeFlyer merged 1 commit into
mavlink:masterfrom
DonLakeFlyer:geomap-heightfield-drape

Conversation

@DonLakeFlyer

Copy link
Copy Markdown
Contributor

Fixes #14823

What this does

Every rendered patch now samples the same continuous terrain heightfield instead of fetching its own per-patch height grid, so neighboring patches can never disagree about the terrain they share — no more cliffs at patch boundaries and no holes while data loads.

  • HeightField — the one continuous heightfield: best-estimate height everywhere by construction. Real data where a tile is stored, ancestor-interpolated estimate where only coarser data exists, zero where nothing is known. There is no "missing" region, only coarser-estimate regions, so coincident vertices of neighboring patches compute bit-identical heights regardless of which patch meshes them.
  • ElevationTilePyramid — bounded in-memory working set of decoded elevation tiles (LRU eviction past 128 tiles, ~32MB) serving queries from the tile itself or its nearest stored ancestor with the sub-window to sample; never a descendant, so coverage is continuous wherever any ancestor data exists.
  • TerrariumTileFetcher — cache-first fetcher over the AWS Open Data Terrain Tiles (terrarium encoding): one slippy PNG tile per patch at every zoom, through QGC's shared tile database with network fallback and write-back on miss. Replaces the fixed-resolution Copernicus pipeline (TerrainHeightSource removed) along with its flat floor / blend band.
  • Skirt sizing — patch skirts scale with the coarsest constraining neighbor LOD delta, hiding seams against coarser neighbors.

Follow-ups tracked separately: #14858 (tile cache capacity, transient skirt sizing), #14859 (retry of failed elevation fetches for resident patches).

Test coverage (local)

All 16 GeoMap test suites, measured with gcovr on an instrumented (-O0) build:

Module totals: lines 89.5% (2375/2654), functions 90.6% (326/360), branches 52.0%

File Line coverage
TileMath.cc 100%
PatchTextureData.cc 100%
PatchGeometry.cc 99%
HeightField.cc 98%
HeightSource.cc 97%
ElevationTilePyramid.cc 96%
GeoMapCamera.cc 96%
SurfaceModel.cc 96%
GeoScene.cc 94%
TileImageSource.cc 93%
CheckerboardTextureData.cc 92%
SurfaceAnalysis.cc 90%
TerrariumTileFetcher.cc 87%
SurfacePatchModel.cc 72%

The SurfacePatchModel gap is the QQuick3D delegate/texture machinery that only executes under a live render loop; the remaining misses elsewhere are defensive branches (e.g. map-engine-unavailable, cache-worker returning a null tile) that can't be arranged deterministically in the test environment.

This comment was marked as resolved.

This comment was marked as resolved.

…or holes

Every rendered patch now samples the same continuous terrain heightfield
instead of fetching its own per-patch height grid, so neighboring patches
can never disagree about the terrain they share - no more cliffs at patch
boundaries and no holes while data loads.

- HeightField: best-estimate height everywhere by construction - real data
  where a tile is stored, ancestor-interpolated estimate where only coarser
  data exists. Coincident vertices of neighboring patches compute
  bit-identical heights regardless of which patch meshes them.
- ElevationTilePyramid: bounded in-memory working set of decoded elevation
  tiles (LRU past 128 tiles, ~32MB) serving queries from the tile itself or
  its nearest stored ancestor with the sub-window to sample.
- TerrariumTileFetcher: cache-first fetcher over the AWS Open Data Terrain
  Tiles (terrarium encoding) - one slippy PNG tile per patch at every zoom,
  network fallback on cache miss with write-back. Replaces the fixed
  resolution Copernicus pipeline (TerrainHeightSource removed) and its flat
  floor / blend band.
- Patch skirts scale with the coarsest constraining neighbor LOD delta to
  hide seams against coarser neighbors.

Fixes mavlink#14823

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated no new comments.

Suppressed comments (3)

src/GeoMap/SurfaceModel.cc:171

  • During capped refinement, this retains the old ancestor while newly added descendants are already rendered (Patch.ready is always true and covered is always false). On the initially flat field those meshes are coplanar, and with terrain their different tessellations intersect, so multiple event-loop passes can show z-fighting even though there is no coverage hole. Keep one layer hidden until the full replacement coverage is resident, or otherwise remove/split the overlapping portion of the old patch before exposing descendants.
        if ((_removalsThisPass >= kMaxPatchRemovalsPerUpdate) || overlapsMissing(it.key())) {
            _removalsDeferred = true;  // stays resident one more pass; follow-up finishes the cull

.clang-format:63

  • This reverses the repository’s documented include order: CODING_STYLE.md:72-78 requires system/STL headers before Qt headers. With this configuration, clang-format will keep producing Qt-first blocks (as seen throughout this PR) and create repo-wide formatting churn. Keep the system category at priority 1 and Qt at priority 2.
  - Regex: '^<Q.*>'      # Qt headers
    Priority: 1
  - Regex: '^<[a-z].*>'  # System/STL headers
    CaseSensitive: true
    Priority: 2

src/GeoMap/SurfaceModel.cc:126

  • On an empty model this cap adds only two disjoint leaves from the fully refined desired set; there is no resident ancestor to cover the remaining missingRects. The model therefore exposes an incomplete sheet for several queued passes on cold start (and after a non-overlapping camera jump), so visible holes remain independent of height availability. Seed a coarse cover first and refine it incrementally, or allow the initial pass to establish complete coverage before enforcing the delegate-add budget.
        if (adds >= kMaxPatchAddsPerUpdate) {
            _addsDeferred = true;
            continue;

@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 57.92208% with 324 lines in your changes missing coverage. Please review.
✅ Project coverage is 33.76%. Comparing base (f29efd3) to head (eb3ebd2).
⚠️ Report is 283 commits behind head on master.

Files with missing lines Patch % Lines
src/GeoMap/TerrariumTileFetcher.cc 46.47% 32 Missing and 97 partials ⚠️
src/GeoMap/SurfacePatchModel.cc 25.37% 17 Missing and 33 partials ⚠️
src/GeoMap/SurfaceModel.cc 63.63% 5 Missing and 31 partials ⚠️
src/GeoMap/HeightField.cc 72.97% 0 Missing and 30 partials ⚠️
src/GeoMap/TileImageSource.cc 22.58% 3 Missing and 21 partials ⚠️
src/GeoMap/PatchGeometry.cc 81.18% 0 Missing and 19 partials ⚠️
src/GeoMap/ElevationTilePyramid.cc 75.00% 2 Missing and 12 partials ⚠️
src/GeoMap/HeightSource.cc 50.00% 2 Missing and 9 partials ⚠️
src/GeoMap/GeoScene.cc 0.00% 0 Missing and 3 partials ⚠️
src/GeoMap/GeoMapCamera.cc 0.00% 0 Missing and 2 partials ⚠️
... and 4 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #14860      +/-   ##
==========================================
+ Coverage   25.47%   33.76%   +8.29%     
==========================================
  Files         769      933     +164     
  Lines       65912    84615   +18703     
  Branches    30495    39120    +8625     
==========================================
+ Hits        16788    28574   +11786     
+ Misses      37285    36838     -447     
- Partials    11839    19203    +7364     
Flag Coverage Δ
unittests 33.76% <57.92%> (+8.29%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/GeoMap/CheckerboardTextureData.cc 63.82% <ø> (ø)
src/GeoMap/ElevationTilePyramid.h 100.00% <100.00%> (ø)
src/GeoMap/GeoMapCamera.h 100.00% <ø> (ø)
src/GeoMap/SurfaceAnalysis.cc 63.35% <ø> (ø)
src/GeoMap/SurfaceModel.h 75.00% <100.00%> (ø)
src/GeoMap/SurfacePatchModel.h 83.33% <100.00%> (ø)
src/GeoMap/TerrariumTileFetcher.h 100.00% <ø> (ø)
src/GeoMap/TileImageSource.h 100.00% <ø> (ø)
src/GeoMap/TileMath.h 75.00% <ø> (ø)
src/GeoMap/HeightField.h 85.71% <85.71%> (ø)
... and 13 more

... and 616 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f1883f9...eb3ebd2. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

Copy link
Copy Markdown
Contributor

Build Results

Platform Status

Platform Status Details
Linux Passed View
Windows Passed View
MacOS Passed View
Android Passed View

All builds passed.

Pre-commit

Check Status Details
pre-commit Failed (non-blocking) View

Pre-commit hooks: 2 passed, 75 failed, 7 skipped.

Test Results

linux-coverage-integration: 43 passed, 0 skipped
linux-coverage-unit: 132 passed, 0 skipped
linux-sanitizers-integration: 25 passed, 0 skipped
linux-sanitizers-unit: 132 passed, 0 skipped
Total: 332 passed, 0 skipped

Code Coverage

Coverage Baseline Change
69.5% 69.0% +0.5%

Artifact Sizes

Artifact Size Δ from master
QGroundControl 252.65 MB -4.38 MB (decrease)
QGroundControl-aarch64 176.49 MB +0.02 MB (increase)
QGroundControl-installer-AMD64 137.92 MB -0.43 MB (decrease)
QGroundControl-installer-AMD64-ARM64 80.43 MB -0.20 MB (decrease)
QGroundControl-installer-ARM64 109.18 MB +0.01 MB (increase)
QGroundControl-linux 84.51 MB -76.53 MB (decrease)
QGroundControl-mac 84.50 MB +0.04 MB (increase)
QGroundControl-x86_64 188.73 MB +0.05 MB (increase)
Total size decreased by 81.41 MB

Updated: 2026-08-13 20:56:08 UTC • Commit: eb3ebd2 • Triggered by: Android

@DonLakeFlyer
DonLakeFlyer merged commit e1f0d6a into mavlink:master Aug 13, 2026
45 of 46 checks passed
@DonLakeFlyer
DonLakeFlyer deleted the geomap-heightfield-drape branch August 13, 2026 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GeoMap: continuous drape terrain design - one heightfield, patches as views, no holes or cliffs ever

2 participants