MVT-postgresql: zoomed-out tiles miss features when storage_crs is projected (e.g. UTM)
Maintainer thường phản hồi trong vòng 1 ngày
@doublebyte1 đang làm issue này rồi.
Từ ngày 28/10/2026.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 58/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ệ
- postgresql, python
- Lĩnh vực
- backend-api-design, databases
Hướng nghiên cứu
Read pygeoapi/provider/mvt_postgresql.py, especially get_tiles() and its geom_filter transform; the issue also links a downstream implementation for comparison. Reproduce with EPSG:25833 data and zoom 0–4 WebMercatorQuad tiles, comparing returned features with geometries intersecting each tile. Done means all intersecting features appear across zoom levels, with a regression test using projected CRS data.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Description
With the MVT-postgresql provider and a table stored in a projected CRS (here EPSG:25833, UTM 33N), the most zoomed-out WebMercatorQuad tiles leave out features that lie inside them.
The cause is in pygeoapi/provider/mvt_postgresql.py, get_tiles() (same in 0.24.0 and master):
geom_filter = geom_column.intersects(
ST_Transform(envelope, self.storage_crs.to_string())
)
ST_TileEnvelope returns a rectangle with only four corners. ST_Transform converts those four points, and the straight lines between them stand in for the tile's edges. In a projected CRS like UTM, a tile's edges are curved, more so the bigger the tile. The straight-line polygon misses slivers of the real tile, and features in those slivers fail the intersects filter.
At zoom 0 it breaks down entirely, because the corners sit at ±180°. With pyproj 3.8.0 (PROJ 9.8.1), EPSG:3857 → EPSG:25833:
(-20037508, -20037508) → (642850, -10531751)
( 20037508, -20037508) → (642850, -10531751)
( 20037508, 20037508) → (642850, 10531751)
(-20037508, 20037508) → (642850, 10531751)
The envelope collapses to the line x = 642850.
Data stored in EPSG:4326 or EPSG:3857 shouldn't be affected, because a WebMercatorQuad tile's edges stay straight in those CRSs.
Steps to Reproduce
- Publish a table stored in EPSG:25833 with the
MVT-postgresqlprovider, e.g. Norway's 357 municipalities. A public pygeoapi 0.24.0 service with exactly this (Kartverket's demo):https://api.datadeling-ogcapi.atgcp1-dev.kartverket.cloud/collections/kommuner/tiles/WebMercatorQuad/{z}/{y}/{x}?f=mvt - Request zoomed-out tiles, e.g.
0/0/0,2/0/2and3/1/4, and decode them. - Compare each tile with the features from
/collections/kommuner/itemsthat intersect it (transformed to EPSG:3857).
| Tile | Should contain | Contains | Missing |
|---|---|---|---|
0/0/0 |
357 | 5 (765 bytes) | all but Balsfjord, Bardu, Karlsøy, Målselv, Tromsø |
1/0/1 |
357 | 357 | |
2/0/2 |
66 | 60 | Beiarn, Lurøy, Rana, Rødøy, Saltdal, Træna |
3/1/4 |
66 | 60 | Beiarn, Lurøy, Rana, Rødøy, Saltdal, Træna |
4/3/8 |
53 | 52 | Lurøy |
5/6/17, 5/7/17 |
4, 52 | 4, 52 |
The five municipalities in the zoom 0 tile are exactly the five that the line x = 642850 crosses. The ones missing at zoom 2–4 all lie between 66.1°N and 67.2°N, along the southern edge of those tiles (66.5°N).
Expected behavior
Every tile contains all the features that intersect it, at every zoom level, whatever the storage CRS.
Screenshots/Tracebacks
No traceback: the tiles are returned with HTTP 200, just with features missing. In QGIS, the zoom 0 tile draws only the five municipalities above, and the zoom 2 tiles leave a gap along 66.5°N.
Environment
- OS: Ubuntu 24.04 (the official
geopython/pygeoapi:0.24.0image, which Kartverket's image builds on) - Python version: 3.12
- pygeoapi version: 0.24.0 (the code is the same on master)
Additional context
A possible fix: add points along the envelope's edges before transforming it (e.g. ST_Segmentize), so the transformed polygon follows the tile's real outline. For tiles too large for that (our patch draws the line at 90° wide, so zoom 0–1), or ones the storage CRS can't express, skip the spatial filter. That's still correct, because ST_AsMVTGeom clips every geometry to the tile and returns NULL for those outside it, and ST_AsMVT skips rows with a NULL geometry.
We patch the provider this way in a downstream image, doing the transform in Python with pyproj so out-of-domain tiles can be caught: https://github.com/henrik716/ogc-api-frontend-workshop/blob/main/backend/mvt_postgresql.py
With the same municipalities in EPSG:25833 and the patch, every tile over Norway from zoom 0 to 7 (207 tiles) contains all the features it should, checked the same way as the table above.
Happy to open a PR with this approach (or another you prefer), including a test with data in a projected CRS.
- Ngôn ngữ chính
- Python
- Star
- 628
- Fork
- 339
- Merge trung bình
- 4 ngày 3 giờ
- Pull request đã merge (30 ngày)
- 11
Chuẩn bị môi trường
- Có Dockerfile hoặc tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- 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.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của geopython/pygeoapi
-
Make HTML-Escaping optionalĐang mởenhancement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
Maintainer thường phản hồi trong vòng 1 ngày
-
the query parameter limit is not working for jobsCó thể làm lại được Pull request cho issue này đã bị đóng mà không được merge. Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Performance issues affecting wide table sourcesCó thể đã có người làm @reinvantveer đã nhận 2 ngày trước. Đang mởbug
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 55/100
Maintainer thường phản hồi trong vòng 1 ngày
-
ESRI provider tests: mock ArcGIS HTTP responses instead of depending on sampleserver6Có thể làm lại được @aaditya755 đã nhận 9 ngày trước và không có pull request nào đang mở. Đang mởenhancement
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 38/100
geopython/pygeoapi#2448 · 4 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
get_collection_map does not parse the transparent query parameterCó thể đã có người làm @tomkralidis đã nhận 11 ngày trước. Đang mởbug OGC API - Maps
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 25/100
geopython/pygeoapi#2444 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của geopython/pygeoapi
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 86/100
-
Validate finite output clamping limits in StepPredictor.prepare_clamping_params to prevent silent NaN/Inf propagationCó thể đã có người làm @muskan-creates352 đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
mllam/neural-lam#848 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
infra
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
fab-geocommuns/RNB-coeur#1166 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
deepspeedai/DeepSpeed#8951 ·
Maintainer thường phản hồi trong vòng 2 ngày