Skip to content

Broken polygon geometries when using postgis provider, while mvt_postgis renders correctly #1104

Description

@NivGreenstein

Summary

I am using Tegola with OSM data downloaded from Geofabrik and imported into PostGIS using osm2pgsql flex Lua.

When serving the data with the regular Tegola postgis provider, the polygon geometries are rendered broken / distorted / unusable in QGIS.

When serving the same table using mvt_postgis, the geometries render correctly.

This happens across the dataset, not only for a single isolated feature.

Expected behavior

Polygon and multipolygon building geometries should render correctly when using the postgis provider.

Actual behavior

When using:

type = "postgis"

the geometries are rendered incorrectly in QGIS.

When using:

type = "mvt_postgis"

against the same PostGIS table, the geometries render correctly.

This suggests that the source geometries are probably valid, and the issue may be related to Tegola's native postgis provider geometry handling / clipping / encoding path.

Environment

  • Tegola version: latest from the repository
  • PostgreSQL:
PostgreSQL 17.7 (Debian 17.7-3.pgdg12+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 12.2.0-14+deb12u1) 12.2.0, 64-bit
  • PostGIS:
POSTGIS="3.6.1 f533623" [EXTENSION] PGSQL="170" GEOS="3.11.1-CAPI-1.17.1" PROJ="9.1.1 NETWORK_ENABLED=OFF URL_ENDPOINT=https://cdn.proj.org USER_WRITABLE_DIRECTORY=/var/lib/postgresql/.local/share/proj DATABASE_PATH=/usr/share/proj/proj.db" (compiled against PROJ 9.1.1) LIBXML="2.9.14" LIBJSON="0.16" LIBPROTOBUF="1.4.1" WAGYU="0.5.0 (Internal)"
  • Architecture: aarch64 / arm64
  • Data source: Geofabrik Greece .osm.pbf
  • Import tool: osm2pgsql flex Lua
  • Geometry projection: EPSG:3857
  • Client: QGIS
  • Example coordinate observed in QGIS:
2626966.8, 4645618.6

This coordinate is consistent with EPSG:3857 / WebMercator meters.

Tegola config using postgis

tile_buffer = 64

[webserver]
port = ":8080"

[cache]
type="file"
basepath="/tmp/tegola"

[[providers]]
name = "test_postgis"
type = "postgis"
uri = "postgres://postgres:postgres@localhost:5432/tegola"

    [[providers.layers]]
    name = "buildings"
    tablename = "public.buildings"
    geometry_fieldname = "geom"
    id_fieldname = "id"

[[maps]]
name = "zoning"
tile_buffer = 64

    [[maps.layers]]
    provider_layer = "test_postgis.buildings"
    min_zoom = 14
    max_zoom = 18

osm2pgsql Lua config

local SRID = 3857

local tables = {}

local counter = 1

tables.buildings = osm2pgsql.define_table({
    name = 'buildings',
    ids = { type = 'area', id_column = 'osm_id' },
    columns = {
        { column = 'id', type = 'int8' },
        { column = 'geom', type = 'polygon', projection = SRID },
        { column = 'relative_feature_height', type = 'real' },
        { column = 'all_entity_names', type = 'text' },
        { column = 'building_type', type = 'text' },
        { column = 'entity', type = 'text' },
        { column = 'date', sql_type = 'timestamp' }
    }
})

function insert_safely(table_key, insert_object)
    if not tables or not tables[table_key] then
        error("Table '" .. tostring(table_key) .. "' does not exist.")
    end

    local inserted, message, column, object = tables[table_key]:insert(insert_object)

    if not inserted then
        print("Insert failed: ", message)
        print("column ", column)
        print("object ", object)
    end

    return true
end

function random_double(min, max)
    return min + math.random() * (max - min)
end

function unix_to_pg_timestamp(unix_time)
    return os.date("%Y-%m-%d %H:%M:%S", unix_time)
end

function osm2pgsql.process_way(object)
    local buildingType = object.tags['building']
    local name = object.tags['name']

    if object.is_closed and buildingType then
        insert_safely("buildings", {
            id = counter,
            entity = name,
            geom = object:as_polygon(),
            building_type = buildingType,
            all_entity_names = name,
            relative_feature_height = random_double(0, 1000),
            date = unix_to_pg_timestamp(object.timestamp)
        })
    end

    counter = counter + 1
end

Notes about the data

The data is polygonal OSM building data imported with:

object:as_polygon()

The table may contain both POLYGON and MULTIPOLYGON geometries.

Reproduction steps

  1. Download Greece OSM data from Geofabrik.
  2. Import the data into PostGIS using osm2pgsql flex and the Lua file above.
  3. Start Tegola using the postgis provider config above.
  4. Open the Tegola MVT layer in QGIS.
  5. Observe that the building geometries are broken / distorted.
  6. Switch the provider to mvt_postgis using the same PostGIS table.
  7. Observe that the geometries render correctly.

Screenshots

Image Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions