MVT-postgresql: zoomed-out tiles miss features when storage_crs is projected (e.g. UTM)
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 58/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- postgresql, python
調査の方向性
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.
索引モデルが issue の本文から書いたものです。
説明
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.
- 主要言語
- Python
- スター
- 628
- フォーク
- 339
- 平均マージ
- 4日 9時間
- マージ済み PR(30日)
- 8
環境構築
- Dockerfile または Docker Compose ファイルあり
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
geopython/pygeoapi のほかの issue
-
enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
メンテナーはふだん 1 日以内に返信
-
the query parameter limit is not working for jobs再び着手できるかも このイシューのプルリクエストはマージされずにクローズされました。 オープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
メンテナーはふだん 1 日以内に返信
-
Performance issues affecting wide table sources対応中かも @reinvantveer が今日担当しました。 オープンbug
難易度 3/5 1〜2日 初心者へのやさしさ 55/100
メンテナーはふだん 1 日以内に返信
-
refactor flask/starlette/django testing対応中かも @tomkralidis が 1 日前に担当しました。 オープンenhancement tests
geopython/pygeoapi#2468 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
ESRI provider tests: mock ArcGIS HTTP responses instead of depending on sampleserver6再び着手できるかも @aaditya755 が 8 日前に担当しましたが、オープン中のプルリクエストはありません。 オープンenhancement
難易度 4/5 3〜5日 初心者へのやさしさ 38/100
geopython/pygeoapi#2448 · コメント 4 件 ·
メンテナーはふだん 1 日以内に返信
geopython/pygeoapi の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
btclib-org/btclib-ecc#404 ·
-
changelog investigate
難易度 1/5 1時間未満 初心者へのやさしさ 78/100
ramnes/notion-sdk-py#430 ·
-
bug good first issue
難易度 1/5 1時間未満 初心者へのやさしさ 91/100
hyeonjoong/debussy#56 ·
-
README устарел: статус проекта, версия 0.1.0 при описании тега v0.1.1, семантика фолбэка поискаオープンdocumentation
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
Lagrunge82/yandex-mail-mcp#43 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
liberation-data/drivine-python#252 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信