I have a GeoJSON file with latlong polygons for the US state of New Mexico. I've loaded them into Postgres, using the geography column type.

For some of the polygons, I get an error when calling ST_PointOnSurface(...::geometry): "lwgeom_pointonsurface: GEOS Error: TopologyException: Input geom 1 is invalid: Self-intersection at or near point ..."

I get this error in Postgres 13 with PostGIS 3.0. (I don't get this error with PostGIS 3.1, but our production DB is stuck on PostGIS 3.0 for now.)

I don't get errors when calling ST_Intersects on the same polygons. (When calling ST_Intersects, I pass in the polygon as a geography without casting to geometry; I don't know if that makes much of a difference for New Mexico.)

Running ST_IsValidReason(...::geometry) on all the polygons, I see that ~10% of them have errors. All the error are "Ring Self-intersection [..., ...]".

I noticed there's an ST_MakeValid function. I'm considering calling ST_MakeValid(...::geometry)::geography when loading them into Postgres.

Is that generally a safe thing to do? I don't understand this stuff well, so I'm worried about subtly masking bugs or subtly corrupting the data.


  • 3
    Try constructing a "bow tie" -- swap the second and third vertex of a simple rectangle -- and see what happens. If it constructs two triangles to make the geometry valid, is that "correct"? You are right to be concerned about masking data errors. Any automated error mechanism is only as good as the worst invalid geometry it fixes correctly.
    – Vince
    Commented Mar 28, 2022 at 0:55

2 Answers 2


Ring self-intersection errors are often caused by "inverted polygons" - i.e. polygons with holes represented by the shell self-touching, rather than by a separate hole ring (which is the only representation the OGC model allows).

If this is the cause of the invalidities, then it should be safe to use ST_MakeValid.

Other kinds of invalidity are potentially safe as well. It might help if you can post some data examples. Or try it out. You could use ST_Area on the original and fixed geometries to see if the geometry has changed too much.


Holes in polygons are represented by 1) a complete polygon and 2) an inner ring representing the hole. To represent a rectangle with a triangle hole, the outer ring is a complete rectangle and the inner ring is a triangle.

Geography uses great circle arcs while geometry uses straight lines to connect points.

Let's consider the following polygon: polygon((0 50,50 50, 50 -50, 0 -50, 0 50),(25 25, 25 51, 26 25, 25 25)), which contains a rectangle whose declared northern coordinate is +50, and an hole whose northern coordinate is +51

When considered as a geometry, the polygon is invalid with a self intersection. When considered as a geography, it is perfectly valid since +51 is well below the great circle arc connecting 0 50 and 50 50 enter image description here

If you have to cast to geometry, the clean solution here is to segmentize the geography before converting

select st_segmentize(geog,100)::geometry

You can make the output "valid", which would make part of the hole as a solid part...

select st_makevalid(geog::geometry)

enter image description here

or you can call buffer(geog,0) to add the "missing" vertex on the outer ring - even though it wrecks the great circle arc!

enter image description here

When working over a small area, where the notion of great circle doesn't matter much, I apply a buffer(geog,0) to get rid of these issues. For larger areas I tend to segmentize, or rather to remain in geography as much as possible - since the issue occurs only when casting / projecting. Let's note that the same behavior occurs when the inner/outer point of contact is slightly moved outside of the outer ring because of grid snapping during (re)projection.


Following @DrJTS and @PaulRamsey comments, it is possible to get a much better fix of an invalid geography. The implementation below works only for geographies in the northern hemisphere (EPSG ESRI 102034) or in the southern hemisphere (EPSG ESRI 102036) but not across.

The bad geography is a rectangle with a hole going across its northern arc, everything within the northern hemisphere.

with src(geog) as (values ('polygon((0 40,50 40, 50 20, 0 20, 0 40),(25 25, 25 51, 26 25, 25 25))'::geography))
    st_isValid(st_transform(geog::geometry,102034)) isvalid_gnomonic,
     4326)::geography  as valid_gnomonic_structure
FROM src;

The hole is now only a hole, no more transformation from hole to solid (thanks to the method=structure argument in st_makevalid), and the general shape/original great circles are respected, thanks to the use of the gnomonic projection.

In the image below, the thick black line is the original geography and the yellow is the fixed geography (segmentized for display)

enter image description here

  • PS: for me, this situation occurs quite often with OSM data, where forested areas are digitized by grid cells and lakes are holes.... since there are lots of forest and lots of lakes in Canada, the inner rings (lakes) touch the outer rings (grid cells) of almost every grid cell
    – JGH
    Commented Mar 28, 2022 at 14:08
  • Great analysis. Seems like what's needed is a "geography-aware densifier", to add shell vertices at points close to problematic holes.
    – dr_jts
    Commented Mar 28, 2022 at 15:47
  • btw, in current PostGIS the ST_MakeValid parameter method=structure produces the same effect as buffer(0), and is preferred because it avoids some problematic edge cases with buffer.
    – dr_jts
    Commented Mar 28, 2022 at 15:49
  • @dr_jts thanks for pointing at the new st_makevalid algorithm!
    – JGH
    Commented Mar 28, 2022 at 15:57
  • For small shapes, running ST_MakeValid in the gnomonic projection is functionally the same as running a "native geography" validity repair, as straight lines in gnomonic are great circles. This doesn't address this valid-in-geography => invalid-in-geometry (and vice versa) situation, but it does give you an "out" to create valid geography. The casting problem is certainly subtle. Commented Mar 28, 2022 at 16:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.