Opened 12 months ago

Closed 9 months ago

#5382 closed defect (wontfix)

ST_AsMVTGeom output is snapped to grid incorrectly

Reported by: mknycha Owned by: pramsey
Priority: medium Milestone: PostGIS 3.1.10
Component: postgis Version: 3.3.x
Keywords: Cc:

Description

Steps to replicate:

  • Run:
    SELECT ST_AsText(ST_AsMVTGeom(
        ST_GeomFromText('POLYGON((0 0, 4 0, 6 6, -1 5, 0 0))'),
        ST_MakeBox2D(ST_Point(0, 0), ST_Point(10, 10)),
        20, 0, true));
    

Actual output:

POLYGON((12 8,8 20,0 20,0 9,12 8))

Expected output:

POLYGON((12 8,8 20,0 20,0 10,12 8))

Explanation:

They way I understand ST_AsMVTGeom works, is that after the transformation and clipping, it snaps the output to the grid of integer points. The problem is, that is does not round the integer coordinates correctly. For example, the output for:

SELECT ST_AsText(ST_AsMVTGeom(
    ST_GeomFromText('POLYGON((0 0, 4 0, 6 6, -3 5, 0 0))'),
    ST_MakeBox2D(ST_Point(0, 0), ST_Point(10, 10)),
    20, 0, false));
              st_astext
--------------------------------------
 POLYGON((12 8,8 20,0 20,-6 10,12 8))
(1 row)

Comparing to the clipped output:

SELECT ST_AsText(ST_AsMVTGeom(
    ST_GeomFromText('POLYGON((0 0, 4 0, 6 6, -3 5, 0 0))'),
    ST_MakeBox2D(ST_Point(0, 0), ST_Point(10, 10)),
    20, 0, true));
             st_astext
------------------------------------
 POLYGON((12 8,8 20,0 20,0 9,12 8))
(1 row)

makes sense, because looking at the bottom edge of the unclipped output, after clipping by the bounding box the bottom left corner should be closer to (0 9) than (0 10).

Outputs comparison (orange unclipped, red clipped): https://i.ibb.co/Gk5Qccn/Screenshot-2023-05-16-at-16-53-39.png

But consider what happens for another output:

SELECT ST_AsText(ST_AsMVTGeom(
    ST_GeomFromText('POLYGON((0 0, 4 0, 6 6, -1 5, 0 0))'),
    ST_MakeBox2D(ST_Point(0, 0), ST_Point(10, 10)),
    20, 0, false));
              st_astext
--------------------------------------
 POLYGON((12 8,8 20,0 20,-2 10,12 8))
(1 row)

Comparing to the clipped one:

SELECT ST_AsText(ST_AsMVTGeom(
    ST_GeomFromText('POLYGON((0 0, 4 0, 6 6, -1 5, 0 0))'),
    ST_MakeBox2D(ST_Point(0, 0), ST_Point(10, 10)),
    20, 0, true));
             st_astext
------------------------------------
 POLYGON((12 8,8 20,0 20,0 9,12 8))
(1 row)

Now, after clipping and before snap to grid, the left bottom corner should be closer to (0 10). That's why I think the output polygon should contain (0 10), instead of (0 9).

Image with outputs comparison (orange unclipped, red clipped): https://i.ibb.co/xs2dWH8/Screenshot-2023-05-16-at-17-07-02.png

Hopefully I got that right, thanks for your open source project

Change History (4)

comment:1 by robe, 12 months ago

Milestone: PostGIS 3.3.3PostGIS 3.1.10

comment:2 by pramsey, 11 months ago

Can you restate this as an example with ST_SnapToGrid that demonstrates the issue you perceive? Under the hood, that is what is being called.

https://github.com/postgis/postgis/blob/6cc527c57a8f13936234da6e19fa3062b18bbfb7/liblwgeom/lwgeom.c#L2166

As far as a problem with MVT, since the snapping is by default happening on a 4096x4096 grid I find it hard to get worked up about which particular grid cell is getting snapped to.

Last edited 11 months ago by pramsey (previous) (diff)

comment:3 by mknycha, 11 months ago

Here is what is being passed to ST_SnapToGrid in my replicated code:

Unclipped: POLYGON ((0 20, 8 20, 12 8, -6 10, 0 20))

Clipped: POLYGON ((0 9.333, 0 20, 8 20, 12 8, 0 9.333))

Maybe the issue is not with snapping to the grid, but happens somewhere earlier?

And regarding

As far as a problem with MVT, since the snapping is by default happening on a 4096x4096 grid I find it hard to get worked up about which particular grid cell is getting snapped to.

I may not fully understand the function, but isn't that the case for the default extent? Because I am passing 20 there.

comment:4 by pramsey, 9 months ago

Resolution: wontfix
Status: newclosed

While there is a grid operation in the pre-processing step, the issue you are seeing is happening in the wagyu library, and I'd posit because wagyu implementation is templated and working in the integer domain, so the result of the clipping intersection if floored to the nearest integral coordinate (which, because the whole coordinate space is flipped in 'y' results in the intersection point being generated downwards). In any event, it's not something we're going to attempt to change, as it's a pixel-level difference that is applied usually on a 4800x4800 grid, in a vendored library.

Interestingly, you can see a completely different behaviour if you push a LINESTRING example through just like your POLYGON, since for non-polygons the GEOS clipping library is used. Another approach would be to use the GEOS new overlay NG with precision set on it. We could probably get "expected" results for you with that approach, but probably at a performance penalty — previous attempts to strip out wagyu for GEOS found that waygyu was quite noticeably faster.

Note: See TracTickets for help on using tickets.