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
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)
or you can call buffer(geog,0)
to add the "missing" vertex on the outer ring - even though it wrecks the great circle arc!
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.
EDIT
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))
select
st_isValid(st_transform(geog::geometry,102034)) isvalid_gnomonic,
st_transform(
st_makeValid(
st_transform(geog::geometry,102034),
'method=structure'),
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)