`render_list --all` with a lon/lat bbox silently skips metatiles at the range edges
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
调研方向
从 src/render_list.c 中的边界框范围计算和渲染循环入手,尤其关注 issue 中描述的两个原因。运行提供的 render_list 命令,并将排入队列的元瓦片与覆盖每个缩放级别范围所需的不同元瓦片进行比较。完成的标准是:请求的边界框得到完整覆盖,包括边缘块,且没有元瓦片被悄然跳过。
由索引模型根据 Issue 内容生成。
描述
Summary
When render_list --all is given --min-lon/--max-lon/--min-lat/--max-lat, it enqueues fewer
metatiles than the bounding box covers. Tiles in the skipped blocks are never rendered, and
render_list exits 0 with no warning.
On one real run over the continental USA at z0-8 it enqueued 32 metatiles where the box needs
41 — a 22% shortfall, concentrated at the south and east edges of the box.
Two independent causes, both in the same block of src/render_list.c. They are separable; the
first accounts for most of the loss.
Version: 0.8.0 (also reproduced by inspection in v0.8.1 and master — the relevant lines
are unchanged).
Cause 1 — the render loop starts at an unaligned coordinate
for (x = min_x; x <= current_max_x; x += METATILE) {
for (y = min_y; y <= current_max_y; y += METATILE) {
src/render_list.c, v0.8.0 lines 468-469 / master lines 469-470
min_x and min_y come straight from lon2tilex() / lat2tiley() and are arbitrary tile
coordinates, not metatile-aligned. enqueue() is then called with them, and the coordinate is
rounded down to the containing metatile downstream — which is why rendering works at all, and why
the problem is invisible rather than an error.
Because the walk starts unaligned, stepping by METATILE can step past the final block. The loop
runs floor((max - min) / METATILE) + 1 times, but the box spans
floor((max - align(min)) / METATILE) + 1 blocks. When min % METATILE is large enough that the
remainder crosses a boundary, the last row or column is never enqueued.
Worked example, z8, y axis, min_y = 87, max_y = 110:
iterations: 87, 95, 103 (111 > 110, stop)
blocks hit: 80, 88, 96
blocks needed: 80, 88, 96, 104 <-- 104 covers rows 104-110, never enqueued
Suggested fix
for (x = min_x - (min_x % METATILE); x <= current_max_x; x += METATILE) {
for (y = min_y - (min_y % METATILE); y <= current_max_y; y += METATILE) {
Both values are non-negative here, so % is well defined. This only ever adds the partial edge
blocks; no currently-rendered tile changes.
Cause 2 — current_max_x is decremented but current_max_y is not
current_max_x = max_x_tmp ? max_x_tmp - 1 : max_x_tmp;
current_max_y = max_y_tmp;
src/render_list.c, v0.8.0 lines 460-461 / master lines 461-462
With lon2tilex() defined as floor((lon + 180) / 360 * 2^z), max_x_tmp is the tile containing
max_lon, so it is part of the box and the - 1 drops it. The decrement is only correct in the
special case where max_lon falls exactly on a tile boundary. The latitude axis gets no equivalent
adjustment, so the two axes disagree about whether the maximum is inclusive.
Worked example, z8, max_lon = -66.9:
lon2tilex(-66.9, 8) = floor(80.42) = 80 tile 80 spans lon [-67.5, -66.09], contains -66.9
current_max_x = 79 tile column 80 excluded
Suggested fix
current_max_x = max_x_tmp;, matching the y axis. Where max_lon does sit exactly on a boundary
this renders one extra column, which seems clearly preferable to dropping a needed one — and it
makes the two axes consistent.
Reproduction
render_list --all --map default --num-threads 4 \
--min-zoom 0 --max-zoom 8 \
--min-lon -125.0 --max-lon -66.9 --min-lat 24.4 --max-lat 49.4
render_list's own log lines give the declared ranges. "Blocks needed" is the count of distinct
metatiles covering that range; "enqueued" is what the loop above produces:
| zoom | declared range | blocks needed | enqueued | short |
|---|---|---|---|---|
| 0 | (0, 0) → (0, 0) | 1 | 1 | — |
| 1 | (0, 0) → (0, 0) | 1 | 1 | — |
| 2 | (0, 1) → (0, 1) | 1 | 1 | — |
| 3 | (1, 2) → (1, 3) | 1 | 1 | — |
| 4 | (2, 5) → (4, 6) | 1 | 1 | — |
| 5 | (4, 10) → (9, 13) | 2 | 1 | 1 |
| 6 | (9, 21) → (19, 27) | 4 | 2 | 2 |
| 7 | (19, 43) → (39, 55) | 6 | 6 | — |
| 8 | (39, 87) → (79, 110) | 24 | 18 | 6 |
| 41 | 32 | 9 |
render_list then reports:
** Message: Metatiles rendered:
** Message: 32 in 2314.60 seconds (0.01/s)
The enqueued column is not measured — it is what Cause 1 predicts — and it matches the reported
total of 32 exactly, as well as the per-zoom request counts in the timing summary. Counting .meta
files on disk afterwards confirms the same gaps.
z7 is unaffected because its remainders happen not to cross a block boundary; that is luck, not
correctness.
Note the table understates the total loss, because the declared ranges it is computed from are
themselves already clipped by Cause 2 — the z8 row is missing column 80 before the loop even runs.
Impact
The skipped blocks lie along the edges of the requested box, so a pre-render pass appears to
succeed while leaving holes exactly where a user panning to the edge of their region will land.
With ModTileMissingRequestTimeout set, those requests return 404 while the render is queued, so
the gap surfaces as intermittent missing tiles well after the pre-render was declared complete.
- 主要语言
- C++
- 星标
- 318
- 派生
- 200
- 平均合并
- 3 天 18 小时
- 30 天内合并 PR
- 35
环境准备
这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
openstreetmap/mod_tile 的其他 Issue
-
难度 1/5 1 小时以内 新手友好度 65/100
openstreetmap/mod_tile#292 · 4 条评论 ·
维护者通常 1 天内回复
-
难度 4/5 3-5 天 新手友好度 35/100
openstreetmap/mod_tile#477 · 7 条评论 ·
维护者通常 1 天内回复
-
难度 4/5 3-5 天 新手友好度 45/100
openstreetmap/mod_tile#466 · 2 条评论 ·
维护者通常 1 天内回复
-
难度 3/5 1-2 天 新手友好度 35/100
openstreetmap/mod_tile#462 · 7 条评论 ·
维护者通常 1 天内回复
-
难度 5/5 一周以上 新手友好度 30/100
openstreetmap/mod_tile#447 · 3 条评论 · 1 个 reaction ·
维护者通常 1 天内回复
查看 openstreetmap/mod_tile 的全部 Issue
相似的 Issue
-
documentation enhancement
难度 2/5 1-3 小时 新手友好度 68/100
维护者通常 1 天内回复
-
enhancement
难度 2/5 1-3 小时 新手友好度 70/100
QuantStack/git2cpp#203 ·
维护者通常 1 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 65/100
BaptisteHudyma/Lamp-Da#588 ·
维护者通常 1 天内回复
-
bug macOS networking
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 2 天内回复
-
难度 2/5 1-3 小时 新手友好度 85/100
docling-project/docling-parse#448 ·
维护者通常 5 天内回复