Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

MVT-postgresql: zoomed-out tiles miss features when storage_crs is projected (e.g. UTM)

オープン
#2,466 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 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 の本文から書いたものです。

説明

bug

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

  1. Publish a table stored in EPSG:25833 with the MVT-postgresql provider, 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
    
  2. Request zoomed-out tiles, e.g. 0/0/0, 2/0/2 and 3/1/4, and decode them.
  3. Compare each tile with the features from /collections/kommuner/items that 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.0 image, 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

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

geopython/pygeoapi のほかの issue

geopython/pygeoapi の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。