`render_list --all` with a lon/lat bbox silently skips metatiles at the range edges

Đã đóng Phù hợp với người mới
#501 2 bình luận 1 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
76/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
c
Lĩnh vực
backend

Hướng nghiên cứu

Bắt đầu với các phép tính phạm vi bounding box và các vòng lặp render trong src/render_list.c, đặc biệt là hai nguyên nhân được mô tả trong issue. Chạy lệnh render_list được cung cấp và so sánh các metatile được đưa vào hàng đợi với các metatile riêng biệt cần thiết để bao phủ từng phạm vi zoom. Công việc được xem là hoàn tất khi bounding box được yêu cầu được bao phủ đầy đủ, bao gồm cả các khối ở rìa, mà không có metatile nào bị âm thầm bỏ qua.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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.

Ngôn ngữ chính
C++
Star
318
Fork
200
Merge trung bình
3 ngày 18 giờ
Pull request đã merge (30 ngày)
35

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của openstreetmap/mod_tile

Tất cả issue của openstreetmap/mod_tile

Issue tương tự

Thêm issue về C++

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.