LCOV - code coverage report
Current view: top level - core/geometry - qgsgeometrymakevalid.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 263 442 59.5 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgsgeometrymakevalid.cpp
       3                 :            :   --------------------------------------
       4                 :            :   Date                 : January 2017
       5                 :            :   Copyright            : (C) 2017 by Martin Dobias
       6                 :            :   Email                : wonder dot sk at gmail dot com
       7                 :            :  ***************************************************************************
       8                 :            :  *                                                                         *
       9                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      10                 :            :  *   it under the terms of the GNU General Public License as published by  *
      11                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      12                 :            :  *   (at your option) any later version.                                   *
      13                 :            :  *                                                                         *
      14                 :            :  ***************************************************************************/
      15                 :            : 
      16                 :            : // The code in this file has been ported from lwgeom library (part of PostGIS).
      17                 :            : // See lwgeom_geos_clean.c for the original code by Sandro Santilli.
      18                 :            : //
      19                 :            : // Ideally one day the implementation will go to GEOS library...
      20                 :            : 
      21                 :            : #include "qgsgeometry.h"
      22                 :            : #include "qgsgeos.h"
      23                 :            : #include "qgslogger.h"
      24                 :            : 
      25                 :            : #include "qgsgeometrycollection.h"
      26                 :            : #include "qgsmultipoint.h"
      27                 :            : #include "qgsmultilinestring.h"
      28                 :            : #include "qgsmultipolygon.h"
      29                 :            : #include "qgspolygon.h"
      30                 :            : 
      31                 :            : #include <memory>
      32                 :            : 
      33                 :            : 
      34                 :            : // ------------ BuildArea stuff ---------------------------------------------------------------------
      35                 :            : 
      36                 :          4 : typedef struct Face_t
      37                 :            : {
      38                 :          4 :   const GEOSGeometry *geom = nullptr;
      39                 :          4 :   GEOSGeometry *env = nullptr;
      40                 :            :   double envarea;
      41                 :            :   struct Face_t *parent; // if this face is an hole of another one, or NULL
      42                 :            : } Face;
      43                 :            : 
      44                 :            : static Face *newFace( const GEOSGeometry *g );
      45                 :            : static void delFace( Face *f );
      46                 :            : static unsigned int countParens( const Face *f );
      47                 :            : static void findFaceHoles( Face **faces, int nfaces );
      48                 :            : 
      49                 :          4 : static Face *newFace( const GEOSGeometry *g )
      50                 :            : {
      51                 :          4 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
      52                 :            : 
      53                 :          4 :   Face *f = new Face;
      54                 :          4 :   f->geom = g;
      55                 :          4 :   f->env = GEOSEnvelope_r( handle, f->geom );
      56                 :          4 :   GEOSArea_r( handle, f->env, &f->envarea );
      57                 :          4 :   f->parent = nullptr;
      58                 :          4 :   return f;
      59                 :            : }
      60                 :            : 
      61                 :          4 : static unsigned int countParens( const Face *f )
      62                 :            : {
      63                 :          4 :   unsigned int pcount = 0;
      64                 :          4 :   while ( f->parent )
      65                 :            :   {
      66                 :          0 :     ++pcount;
      67                 :          0 :     f = f->parent;
      68                 :            :   }
      69                 :          4 :   return pcount;
      70                 :            : }
      71                 :            : 
      72                 :            : // Destroy the face and release memory associated with it
      73                 :          4 : static void delFace( Face *f )
      74                 :            : {
      75                 :          4 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
      76                 :          4 :   GEOSGeom_destroy_r( handle, f->env );
      77                 :          4 :   delete f;
      78                 :          4 : }
      79                 :            : 
      80                 :            : 
      81                 :          2 : static int compare_by_envarea( const void *g1, const void *g2 )
      82                 :            : {
      83                 :          2 :   Face *f1 = *( Face ** )g1;
      84                 :          2 :   Face *f2 = *( Face ** )g2;
      85                 :          2 :   double n1 = f1->envarea;
      86                 :          2 :   double n2 = f2->envarea;
      87                 :            : 
      88                 :          2 :   if ( n1 < n2 ) return 1;
      89                 :          2 :   if ( n1 > n2 ) return -1;
      90                 :          2 :   return 0;
      91                 :          2 : }
      92                 :            : 
      93                 :            : // Find holes of each face
      94                 :          2 : static void findFaceHoles( Face **faces, int nfaces )
      95                 :            : {
      96                 :          2 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
      97                 :            : 
      98                 :            :   // We sort by envelope area so that we know holes are only
      99                 :            :   // after their shells
     100                 :          2 :   qsort( faces, nfaces, sizeof( Face * ), compare_by_envarea );
     101                 :          6 :   for ( int i = 0; i < nfaces; ++i )
     102                 :            :   {
     103                 :          4 :     Face *f = faces[i];
     104                 :          4 :     int nholes = GEOSGetNumInteriorRings_r( handle, f->geom );
     105                 :          4 :     for ( int h = 0; h < nholes; ++h )
     106                 :            :     {
     107                 :          0 :       const GEOSGeometry *hole = GEOSGetInteriorRingN_r( handle, f->geom, h );
     108                 :          0 :       for ( int j = i + 1; j < nfaces; ++j )
     109                 :            :       {
     110                 :          0 :         Face *f2 = faces[j];
     111                 :          0 :         if ( f2->parent )
     112                 :          0 :           continue; // hole already assigned
     113                 :            :         /* TODO: can be optimized as the ring would have the
     114                 :            :          *       same vertices, possibly in different order.
     115                 :            :          *       maybe comparing number of points could already be
     116                 :            :          *       useful.
     117                 :            :          */
     118                 :          0 :         if ( GEOSEquals_r( handle, GEOSGetExteriorRing_r( handle, f2->geom ), hole ) )
     119                 :            :         {
     120                 :          0 :           f2->parent = f;
     121                 :          0 :           break;
     122                 :            :         }
     123                 :          0 :       }
     124                 :          0 :     }
     125                 :          4 :   }
     126                 :          2 : }
     127                 :            : 
     128                 :          2 : static GEOSGeometry *collectFacesWithEvenAncestors( Face **faces, int nfaces )
     129                 :            : {
     130                 :          2 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
     131                 :            : 
     132                 :          2 :   unsigned int ngeoms = 0;
     133                 :          2 :   GEOSGeometry **geoms = new GEOSGeometry*[nfaces];
     134                 :          6 :   for ( int i = 0; i < nfaces; ++i )
     135                 :            :   {
     136                 :          4 :     Face *f = faces[i];
     137                 :          4 :     if ( countParens( f ) % 2 )
     138                 :          0 :       continue; // we skip odd parents geoms
     139                 :          4 :     geoms[ngeoms++] = GEOSGeom_clone_r( handle, f->geom );
     140                 :          4 :   }
     141                 :            : 
     142                 :          2 :   GEOSGeometry *ret = GEOSGeom_createCollection_r( handle, GEOS_MULTIPOLYGON, geoms, ngeoms );
     143                 :          2 :   delete [] geoms;
     144                 :          2 :   return ret;
     145                 :            : }
     146                 :            : 
     147                 :            : Q_NOWARN_UNREACHABLE_PUSH
     148                 :          6 : static GEOSGeometry *LWGEOM_GEOS_buildArea( const GEOSGeometry *geom_in, QString &errorMessage )
     149                 :            : {
     150                 :          6 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
     151                 :            : 
     152                 :          6 :   GEOSGeometry *tmp = nullptr;
     153                 :          6 :   GEOSGeometry *shp = nullptr;
     154                 :          6 :   GEOSGeometry *geos_result = nullptr;
     155                 :          6 :   int srid = GEOSGetSRID_r( handle, geom_in );
     156                 :            : 
     157                 :            :   GEOSGeometry const *vgeoms[1];
     158                 :          6 :   vgeoms[0] = geom_in;
     159                 :            :   try
     160                 :            :   {
     161                 :          6 :     geos_result = GEOSPolygonize_r( handle, vgeoms, 1 );
     162                 :          6 :   }
     163                 :            :   catch ( GEOSException &e )
     164                 :            :   {
     165                 :          0 :     errorMessage = QStringLiteral( "GEOSPolygonize(): %1" ).arg( e.what() );
     166                 :          0 :     return nullptr;
     167                 :          0 :   }
     168                 :            : 
     169                 :            :   // We should now have a collection
     170                 :            : #if PARANOIA_LEVEL > 0
     171                 :            :   if ( GEOSGeometryTypeId_r( handle, geos_result ) != COLLECTIONTYPE )
     172                 :            :   {
     173                 :            :     GEOSGeom_destroy_r( handle, geos_result );
     174                 :            :     errorMessage = "Unexpected return from GEOSpolygonize";
     175                 :            :     return nullptr;
     176                 :            :   }
     177                 :            : #endif
     178                 :            : 
     179                 :          6 :   int ngeoms = GEOSGetNumGeometries_r( handle, geos_result );
     180                 :            : 
     181                 :            :   // No geometries in collection, early out
     182                 :          6 :   if ( ngeoms == 0 )
     183                 :            :   {
     184                 :          3 :     GEOSSetSRID_r( handle, geos_result, srid );
     185                 :          3 :     return geos_result;
     186                 :            :   }
     187                 :            : 
     188                 :            :   // Return first geometry if we only have one in collection,
     189                 :            :   // to avoid the unnecessary Geometry clone below.
     190                 :          3 :   if ( ngeoms == 1 )
     191                 :            :   {
     192                 :          1 :     tmp = ( GEOSGeometry * )GEOSGetGeometryN_r( handle, geos_result, 0 );
     193                 :          1 :     if ( ! tmp )
     194                 :            :     {
     195                 :          0 :       GEOSGeom_destroy_r( handle, geos_result );
     196                 :          0 :       return nullptr;
     197                 :            :     }
     198                 :          1 :     shp = GEOSGeom_clone_r( handle, tmp );
     199                 :          1 :     GEOSGeom_destroy_r( handle, geos_result ); // only safe after the clone above
     200                 :          1 :     GEOSSetSRID_r( handle, shp, srid );
     201                 :          1 :     return shp;
     202                 :            :   }
     203                 :            : 
     204                 :            :   /*
     205                 :            :    * Polygonizer returns a polygon for each face in the built topology.
     206                 :            :    *
     207                 :            :    * This means that for any face with holes we'll have other faces
     208                 :            :    * representing each hole. We can imagine a parent-child relationship
     209                 :            :    * between these faces.
     210                 :            :    *
     211                 :            :    * In order to maximize the number of visible rings in output we
     212                 :            :    * only use those faces which have an even number of parents.
     213                 :            :    *
     214                 :            :    * Example:
     215                 :            :    *
     216                 :            :    *   +---------------+
     217                 :            :    *   |     L0        |  L0 has no parents
     218                 :            :    *   |  +---------+  |
     219                 :            :    *   |  |   L1    |  |  L1 is an hole of L0
     220                 :            :    *   |  |  +---+  |  |
     221                 :            :    *   |  |  |L2 |  |  |  L2 is an hole of L1 (which is an hole of L0)
     222                 :            :    *   |  |  |   |  |  |
     223                 :            :    *   |  |  +---+  |  |
     224                 :            :    *   |  +---------+  |
     225                 :            :    *   |               |
     226                 :            :    *   +---------------+
     227                 :            :    *
     228                 :            :    * See http://trac.osgeo.org/postgis/ticket/1806
     229                 :            :    *
     230                 :            :    */
     231                 :            : 
     232                 :            :   // Prepare face structures for later analysis
     233                 :          2 :   Face **geoms = new Face*[ngeoms];
     234                 :          6 :   for ( int i = 0; i < ngeoms; ++i )
     235                 :          4 :     geoms[i] = newFace( GEOSGetGeometryN_r( handle, geos_result, i ) );
     236                 :            : 
     237                 :            :   // Find faces representing other faces holes
     238                 :          2 :   findFaceHoles( geoms, ngeoms );
     239                 :            : 
     240                 :            :   // Build a MultiPolygon composed only by faces with an
     241                 :            :   // even number of ancestors
     242                 :          2 :   tmp = collectFacesWithEvenAncestors( geoms, ngeoms );
     243                 :            : 
     244                 :            :   // Cleanup face structures
     245                 :          6 :   for ( int i = 0; i < ngeoms; ++i )
     246                 :          4 :     delFace( geoms[i] );
     247                 :          2 :   delete [] geoms;
     248                 :            : 
     249                 :            :   // Faces referenced memory owned by geos_result.
     250                 :            :   // It is safe to destroy geos_result after deleting them.
     251                 :          2 :   GEOSGeom_destroy_r( handle, geos_result );
     252                 :            : 
     253                 :            :   // Run a single overlay operation to dissolve shared edges
     254                 :          2 :   shp = GEOSUnionCascaded_r( handle, tmp );
     255                 :          2 :   if ( !shp )
     256                 :            :   {
     257                 :          0 :     GEOSGeom_destroy_r( handle, tmp );
     258                 :          0 :     return nullptr;
     259                 :            :   }
     260                 :            : 
     261                 :          2 :   GEOSGeom_destroy_r( handle, tmp );
     262                 :            : 
     263                 :          2 :   GEOSSetSRID_r( handle, shp, srid );
     264                 :            : 
     265                 :          2 :   return shp;
     266                 :          6 : }
     267                 :            : Q_NOWARN_UNREACHABLE_POP
     268                 :            : 
     269                 :            : // Return Nth vertex in GEOSGeometry as a POINT.
     270                 :            : // May return NULL if the geometry has NO vertexex.
     271                 :          6 : static GEOSGeometry *LWGEOM_GEOS_getPointN( const GEOSGeometry *g_in, uint32_t n )
     272                 :            : {
     273                 :          6 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
     274                 :            : 
     275                 :            :   uint32_t dims;
     276                 :          6 :   const GEOSCoordSequence *seq_in = nullptr;
     277                 :            :   GEOSCoordSeq seq_out;
     278                 :            :   double val;
     279                 :            :   uint32_t sz;
     280                 :            :   int gn;
     281                 :          6 :   GEOSGeometry *ret = nullptr;
     282                 :            : 
     283                 :          6 :   switch ( GEOSGeomTypeId_r( handle, g_in ) )
     284                 :            :   {
     285                 :            :     case GEOS_MULTIPOINT:
     286                 :            :     case GEOS_MULTILINESTRING:
     287                 :            :     case GEOS_MULTIPOLYGON:
     288                 :            :     case GEOS_GEOMETRYCOLLECTION:
     289                 :            :     {
     290                 :          0 :       for ( gn = 0; gn < GEOSGetNumGeometries_r( handle, g_in ); ++gn )
     291                 :            :       {
     292                 :          0 :         const GEOSGeometry *g = GEOSGetGeometryN_r( handle, g_in, gn );
     293                 :          0 :         ret = LWGEOM_GEOS_getPointN( g, n );
     294                 :          0 :         if ( ret ) return ret;
     295                 :          0 :       }
     296                 :          0 :       break;
     297                 :            :     }
     298                 :            : 
     299                 :            :     case GEOS_POLYGON:
     300                 :            :     {
     301                 :          0 :       ret = LWGEOM_GEOS_getPointN( GEOSGetExteriorRing_r( handle, g_in ), n );
     302                 :          0 :       if ( ret ) return ret;
     303                 :          0 :       for ( gn = 0; gn < GEOSGetNumInteriorRings_r( handle, g_in ); ++gn )
     304                 :            :       {
     305                 :          0 :         const GEOSGeometry *g = GEOSGetInteriorRingN_r( handle, g_in, gn );
     306                 :          0 :         ret = LWGEOM_GEOS_getPointN( g, n );
     307                 :          0 :         if ( ret ) return ret;
     308                 :          0 :       }
     309                 :          0 :       break;
     310                 :            :     }
     311                 :            : 
     312                 :            :     case GEOS_POINT:
     313                 :            :     case GEOS_LINESTRING:
     314                 :            :     case GEOS_LINEARRING:
     315                 :          6 :       break;
     316                 :            : 
     317                 :            :   }
     318                 :            : 
     319                 :          6 :   seq_in = GEOSGeom_getCoordSeq_r( handle, g_in );
     320                 :          6 :   if ( ! seq_in ) return nullptr;
     321                 :          6 :   if ( ! GEOSCoordSeq_getSize_r( handle, seq_in, &sz ) ) return nullptr;
     322                 :          6 :   if ( ! sz ) return nullptr;
     323                 :            : 
     324                 :          6 :   if ( ! GEOSCoordSeq_getDimensions_r( handle, seq_in, &dims ) ) return nullptr;
     325                 :            : 
     326                 :          6 :   seq_out = GEOSCoordSeq_create_r( handle, 1, dims );
     327                 :          6 :   if ( ! seq_out ) return nullptr;
     328                 :            : 
     329                 :          6 :   if ( ! GEOSCoordSeq_getX_r( handle, seq_in, n, &val ) ) return nullptr;
     330                 :          6 :   if ( ! GEOSCoordSeq_setX_r( handle, seq_out, n, val ) ) return nullptr;
     331                 :          6 :   if ( ! GEOSCoordSeq_getY_r( handle, seq_in, n, &val ) ) return nullptr;
     332                 :          6 :   if ( ! GEOSCoordSeq_setY_r( handle, seq_out, n, val ) ) return nullptr;
     333                 :          6 :   if ( dims > 2 )
     334                 :            :   {
     335                 :          0 :     if ( ! GEOSCoordSeq_getZ_r( handle, seq_in, n, &val ) ) return nullptr;
     336                 :          0 :     if ( ! GEOSCoordSeq_setZ_r( handle, seq_out, n, val ) ) return nullptr;
     337                 :          0 :   }
     338                 :            : 
     339                 :          6 :   return GEOSGeom_createPoint_r( handle, seq_out );
     340                 :          6 : }
     341                 :            : 
     342                 :            : 
     343                 :            : static bool lwline_make_geos_friendly( QgsLineString *line );
     344                 :            : static bool lwpoly_make_geos_friendly( QgsPolygon *poly );
     345                 :            : static bool lwcollection_make_geos_friendly( QgsGeometryCollection *g );
     346                 :            : 
     347                 :            : 
     348                 :            : // Ensure the geometry is "structurally" valid (enough for GEOS to accept it)
     349                 :          1 : static bool lwgeom_make_geos_friendly( QgsAbstractGeometry *geom )
     350                 :            : {
     351                 :          1 :   QgsDebugMsgLevel( QStringLiteral( "lwgeom_make_geos_friendly enter (type %1)" ).arg( geom->wkbType() ), 3 );
     352                 :          1 :   switch ( QgsWkbTypes::flatType( geom->wkbType() ) )
     353                 :            :   {
     354                 :            :     case QgsWkbTypes::Point:
     355                 :            :     case QgsWkbTypes::MultiPoint:
     356                 :            :       // a point is always valid
     357                 :          0 :       return true;
     358                 :            :       break;
     359                 :            : 
     360                 :            :     case QgsWkbTypes::LineString:
     361                 :            :       // lines need at least 2 points
     362                 :          1 :       return lwline_make_geos_friendly( qgsgeometry_cast<QgsLineString *>( geom ) );
     363                 :            :       break;
     364                 :            : 
     365                 :            :     case QgsWkbTypes::Polygon:
     366                 :            :       // polygons need all rings closed and with npoints > 3
     367                 :          0 :       return lwpoly_make_geos_friendly( qgsgeometry_cast<QgsPolygon *>( geom ) );
     368                 :            :       break;
     369                 :            : 
     370                 :            :     case QgsWkbTypes::MultiLineString:
     371                 :            :     case QgsWkbTypes::MultiPolygon:
     372                 :            :     case QgsWkbTypes::GeometryCollection:
     373                 :          0 :       return lwcollection_make_geos_friendly( qgsgeometry_cast<QgsGeometryCollection *>( geom ) );
     374                 :            :       break;
     375                 :            : 
     376                 :            :     default:
     377                 :          0 :       QgsDebugMsg( QStringLiteral( "lwgeom_make_geos_friendly: unsupported input geometry type: %1" ).arg( geom->wkbType() ) );
     378                 :          0 :       break;
     379                 :            :   }
     380                 :          0 :   return false;
     381                 :          1 : }
     382                 :            : 
     383                 :            : 
     384                 :          0 : static bool ring_make_geos_friendly( QgsCurve *ring )
     385                 :            : {
     386                 :          0 :   if ( ring->nCoordinates() == 0 )
     387                 :          0 :     return false;
     388                 :            : 
     389                 :            :   // earlier we allowed in only geometries with straight segments
     390                 :          0 :   QgsLineString *linestring = qgsgeometry_cast<QgsLineString *>( ring );
     391                 :            :   Q_ASSERT_X( linestring, "ring_make_geos_friendly", "ring was not a linestring" );
     392                 :            : 
     393                 :            :   // close the ring if not already closed (2d only)
     394                 :            : 
     395                 :          0 :   QgsPoint p1 = linestring->startPoint(), p2 = linestring->endPoint();
     396                 :          0 :   if ( p1.x() != p2.x() || p1.y() != p2.y() )
     397                 :          0 :     linestring->addVertex( p1 );
     398                 :            : 
     399                 :            :   // must have at least 4 coordinates to be accepted by GEOS
     400                 :            : 
     401                 :          0 :   while ( linestring->nCoordinates() < 4 )
     402                 :          0 :     linestring->addVertex( p1 );
     403                 :            : 
     404                 :          0 :   return true;
     405                 :          0 : }
     406                 :            : 
     407                 :            : // Make sure all rings are closed and have > 3 points.
     408                 :          0 : static bool lwpoly_make_geos_friendly( QgsPolygon *poly )
     409                 :            : {
     410                 :            :   // If the polygon has no rings there's nothing to do
     411                 :            :   // TODO: in qgis representation there always is exterior ring
     412                 :            :   //if ( ! poly->nrings ) return true;
     413                 :            : 
     414                 :            :   // All rings must be closed and have > 3 points
     415                 :          0 :   for ( int i = 0; i < poly->numInteriorRings(); i++ )
     416                 :            :   {
     417                 :          0 :     if ( !ring_make_geos_friendly( qgsgeometry_cast<QgsCurve *>( poly->interiorRing( i ) ) ) )
     418                 :          0 :       return false;
     419                 :          0 :   }
     420                 :            : 
     421                 :          0 :   return true;
     422                 :          0 : }
     423                 :            : 
     424                 :            : // Need NO or >1 points. Duplicate first if only one.
     425                 :          1 : static bool lwline_make_geos_friendly( QgsLineString *line )
     426                 :            : {
     427                 :          1 :   if ( line->numPoints() == 1 ) // 0 is fine, 2 is fine
     428                 :            :   {
     429                 :          1 :     line->addVertex( line->startPoint() );
     430                 :          1 :   }
     431                 :          1 :   return true;
     432                 :          0 : }
     433                 :            : 
     434                 :          0 : static bool lwcollection_make_geos_friendly( QgsGeometryCollection *g )
     435                 :            : {
     436                 :          0 :   for ( int i = 0; i < g->numGeometries(); i++ )
     437                 :            :   {
     438                 :          0 :     if ( !lwgeom_make_geos_friendly( g->geometryN( i ) ) )
     439                 :          0 :       return false;
     440                 :          0 :   }
     441                 :            : 
     442                 :          0 :   return true;
     443                 :          0 : }
     444                 :            : 
     445                 :            : 
     446                 :            : // Fully node given linework
     447                 :          6 : static GEOSGeometry *LWGEOM_GEOS_nodeLines( const GEOSGeometry *lines )
     448                 :            : {
     449                 :          6 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
     450                 :            : 
     451                 :            :   // Union with first geometry point, obtaining full noding
     452                 :            :   // and dissolving of duplicated repeated points
     453                 :            :   //
     454                 :            :   // TODO: substitute this with UnaryUnion?
     455                 :            : 
     456                 :          6 :   GEOSGeometry *point = LWGEOM_GEOS_getPointN( lines, 0 );
     457                 :          6 :   if ( ! point )
     458                 :          0 :     return nullptr;
     459                 :            : 
     460                 :          6 :   GEOSGeometry *noded = nullptr;
     461                 :            :   try
     462                 :            :   {
     463                 :          6 :     noded = GEOSUnion_r( handle, lines, point );
     464                 :          6 :   }
     465                 :            :   catch ( GEOSException & )
     466                 :            :   {
     467                 :            :     // no need to do anything here - we'll return nullptr anyway
     468                 :          0 :   }
     469                 :          6 :   GEOSGeom_destroy_r( handle, point );
     470                 :          6 :   return noded;
     471                 :          6 : }
     472                 :            : 
     473                 :            : // Will return NULL on error (expect error handler being called by then)
     474                 :            : Q_NOWARN_UNREACHABLE_PUSH
     475                 :          3 : static GEOSGeometry *LWGEOM_GEOS_makeValidPolygon( const GEOSGeometry *gin, QString &errorMessage )
     476                 :            : {
     477                 :          3 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
     478                 :            : 
     479                 :            :   GEOSGeom gout;
     480                 :            :   GEOSGeom geos_bound;
     481                 :            :   GEOSGeom geos_cut_edges, geos_area, collapse_points;
     482                 :            :   GEOSGeometry *vgeoms[3]; // One for area, one for cut-edges
     483                 :          3 :   unsigned int nvgeoms = 0;
     484                 :            : 
     485                 :            :   Q_ASSERT( GEOSGeomTypeId_r( handle, gin ) == GEOS_POLYGON ||
     486                 :            :             GEOSGeomTypeId_r( handle, gin ) == GEOS_MULTIPOLYGON );
     487                 :            : 
     488                 :          3 :   geos_bound = GEOSBoundary_r( handle, gin );
     489                 :          3 :   if ( !geos_bound )
     490                 :          0 :     return nullptr;
     491                 :            : 
     492                 :            :   // Use noded boundaries as initial "cut" edges
     493                 :            : 
     494                 :          3 :   geos_cut_edges = LWGEOM_GEOS_nodeLines( geos_bound );
     495                 :          3 :   if ( !geos_cut_edges )
     496                 :            :   {
     497                 :          0 :     GEOSGeom_destroy_r( handle, geos_bound );
     498                 :          0 :     errorMessage = QStringLiteral( "LWGEOM_GEOS_nodeLines() failed" );
     499                 :          0 :     return nullptr;
     500                 :            :   }
     501                 :            : 
     502                 :            :   // NOTE: the noding process may drop lines collapsing to points.
     503                 :            :   //       We want to retrieve any of those
     504                 :            :   {
     505                 :          3 :     GEOSGeometry *pi = nullptr;
     506                 :          3 :     GEOSGeometry *po = nullptr;
     507                 :            : 
     508                 :            :     try
     509                 :            :     {
     510                 :          3 :       pi = GEOSGeom_extractUniquePoints_r( handle, geos_bound );
     511                 :          3 :     }
     512                 :            :     catch ( GEOSException &e )
     513                 :            :     {
     514                 :          0 :       GEOSGeom_destroy_r( handle, geos_bound );
     515                 :          0 :       errorMessage = QStringLiteral( "GEOSGeom_extractUniquePoints(): %1" ).arg( e.what() );
     516                 :          0 :       return nullptr;
     517                 :          0 :     }
     518                 :            : 
     519                 :            :     try
     520                 :            :     {
     521                 :          3 :       po = GEOSGeom_extractUniquePoints_r( handle, geos_cut_edges );
     522                 :          3 :     }
     523                 :            :     catch ( GEOSException &e )
     524                 :            :     {
     525                 :          0 :       GEOSGeom_destroy_r( handle, geos_bound );
     526                 :          0 :       GEOSGeom_destroy_r( handle, pi );
     527                 :          0 :       errorMessage = QStringLiteral( "GEOSGeom_extractUniquePoints(): %1" ).arg( e.what() );
     528                 :          0 :       return nullptr;
     529                 :          0 :     }
     530                 :            : 
     531                 :            :     try
     532                 :            :     {
     533                 :          3 :       collapse_points = GEOSDifference_r( handle, pi, po );
     534                 :          3 :     }
     535                 :            :     catch ( GEOSException &e )
     536                 :            :     {
     537                 :          0 :       GEOSGeom_destroy_r( handle, geos_bound );
     538                 :          0 :       GEOSGeom_destroy_r( handle, pi );
     539                 :          0 :       GEOSGeom_destroy_r( handle, po );
     540                 :          0 :       errorMessage = QStringLiteral( "GEOSDifference(): %1" ).arg( e.what() );
     541                 :          0 :       return nullptr;
     542                 :          0 :     }
     543                 :            : 
     544                 :          3 :     GEOSGeom_destroy_r( handle, pi );
     545                 :          3 :     GEOSGeom_destroy_r( handle, po );
     546                 :            :   }
     547                 :          3 :   GEOSGeom_destroy_r( handle, geos_bound );
     548                 :            : 
     549                 :            :   // And use an empty geometry as initial "area"
     550                 :            :   try
     551                 :            :   {
     552                 :          3 :     geos_area = GEOSGeom_createEmptyPolygon_r( handle );
     553                 :          3 :   }
     554                 :            :   catch ( GEOSException &e )
     555                 :            :   {
     556                 :          0 :     errorMessage = QStringLiteral( "GEOSGeom_createEmptyPolygon(): %1" ).arg( e.what() );
     557                 :          0 :     GEOSGeom_destroy_r( handle, geos_cut_edges );
     558                 :          0 :     return nullptr;
     559                 :          0 :   }
     560                 :            : 
     561                 :            :   // See if an area can be build with the remaining edges
     562                 :            :   // and if it can, symdifference with the original area.
     563                 :            :   // Iterate this until no more polygons can be created
     564                 :            :   // with left-over edges.
     565                 :          6 :   while ( GEOSGetNumGeometries_r( handle, geos_cut_edges ) )
     566                 :            :   {
     567                 :          6 :     GEOSGeometry *new_area = nullptr;
     568                 :          6 :     GEOSGeometry *new_area_bound = nullptr;
     569                 :          6 :     GEOSGeometry *symdif = nullptr;
     570                 :          6 :     GEOSGeometry *new_cut_edges = nullptr;
     571                 :            : 
     572                 :            :     // ASSUMPTION: cut_edges should already be fully noded
     573                 :            : 
     574                 :            :     try
     575                 :            :     {
     576                 :          6 :       new_area = LWGEOM_GEOS_buildArea( geos_cut_edges, errorMessage );
     577                 :          6 :     }
     578                 :            :     catch ( GEOSException &e )
     579                 :            :     {
     580                 :          0 :       GEOSGeom_destroy_r( handle, geos_cut_edges );
     581                 :          0 :       GEOSGeom_destroy_r( handle, geos_area );
     582                 :          0 :       errorMessage = QStringLiteral( "LWGEOM_GEOS_buildArea() threw an error: %1" ).arg( e.what() );
     583                 :          0 :       return nullptr;
     584                 :          0 :     }
     585                 :            : 
     586                 :          6 :     if ( GEOSisEmpty_r( handle, new_area ) )
     587                 :            :     {
     588                 :            :       // no more rings can be build with the edges
     589                 :          3 :       GEOSGeom_destroy_r( handle, new_area );
     590                 :          3 :       break;
     591                 :            :     }
     592                 :            : 
     593                 :            :     // We succeeded in building a ring !
     594                 :            : 
     595                 :            :     // Save the new ring boundaries first (to compute
     596                 :            :     // further cut edges later)
     597                 :            :     try
     598                 :            :     {
     599                 :          3 :       new_area_bound = GEOSBoundary_r( handle, new_area );
     600                 :          3 :     }
     601                 :            :     catch ( GEOSException &e )
     602                 :            :     {
     603                 :            :       // We did check for empty area already so
     604                 :            :       // this must be some other error
     605                 :          0 :       errorMessage = QStringLiteral( "GEOSBoundary() threw an error: %1" ).arg( e.what() );
     606                 :          0 :       GEOSGeom_destroy_r( handle, new_area );
     607                 :          0 :       GEOSGeom_destroy_r( handle, geos_area );
     608                 :          0 :       return nullptr;
     609                 :          0 :     }
     610                 :            : 
     611                 :            :     // Now symdiff new and old area
     612                 :            :     try
     613                 :            :     {
     614                 :          3 :       symdif = GEOSSymDifference_r( handle, geos_area, new_area );
     615                 :          3 :     }
     616                 :            :     catch ( GEOSException &e )
     617                 :            :     {
     618                 :          0 :       GEOSGeom_destroy_r( handle, geos_cut_edges );
     619                 :          0 :       GEOSGeom_destroy_r( handle, new_area );
     620                 :          0 :       GEOSGeom_destroy_r( handle, new_area_bound );
     621                 :          0 :       GEOSGeom_destroy_r( handle, geos_area );
     622                 :          0 :       errorMessage = QStringLiteral( "GEOSSymDifference() threw an error: %1" ).arg( e.what() );
     623                 :          0 :       return nullptr;
     624                 :          0 :     }
     625                 :            : 
     626                 :          3 :     GEOSGeom_destroy_r( handle, geos_area );
     627                 :          3 :     GEOSGeom_destroy_r( handle, new_area );
     628                 :          3 :     geos_area = symdif;
     629                 :          3 :     symdif = nullptr;
     630                 :            : 
     631                 :            :     // Now let's re-set geos_cut_edges with what's left
     632                 :            :     // from the original boundary.
     633                 :            :     // ASSUMPTION: only the previous cut-edges can be
     634                 :            :     //             left, so we don't need to reconsider
     635                 :            :     //             the whole original boundaries
     636                 :            :     //
     637                 :            :     // NOTE: this is an expensive operation.
     638                 :            : 
     639                 :            :     try
     640                 :            :     {
     641                 :          3 :       new_cut_edges = GEOSDifference_r( handle, geos_cut_edges, new_area_bound );
     642                 :          3 :     }
     643                 :            :     catch ( GEOSException &e )
     644                 :            :     {
     645                 :          0 :       GEOSGeom_destroy_r( handle, geos_cut_edges );
     646                 :          0 :       GEOSGeom_destroy_r( handle, new_area_bound );
     647                 :          0 :       GEOSGeom_destroy_r( handle, geos_area );
     648                 :          0 :       errorMessage = QStringLiteral( "GEOSDifference() threw an error: %1" ).arg( e.what() );
     649                 :          0 :       return nullptr;
     650                 :          0 :     }
     651                 :          3 :     GEOSGeom_destroy_r( handle, geos_cut_edges );
     652                 :          3 :     GEOSGeom_destroy_r( handle, new_area_bound );
     653                 :          3 :     geos_cut_edges = new_cut_edges;
     654                 :            :   }
     655                 :            : 
     656                 :          3 :   if ( !GEOSisEmpty_r( handle, geos_area ) )
     657                 :            :   {
     658                 :          3 :     vgeoms[nvgeoms++] = geos_area;
     659                 :          3 :   }
     660                 :            :   else
     661                 :            :   {
     662                 :          0 :     GEOSGeom_destroy_r( handle, geos_area );
     663                 :            :   }
     664                 :            : 
     665                 :          3 :   if ( !GEOSisEmpty_r( handle, geos_cut_edges ) )
     666                 :            :   {
     667                 :          1 :     vgeoms[nvgeoms++] = geos_cut_edges;
     668                 :          1 :   }
     669                 :            :   else
     670                 :            :   {
     671                 :          2 :     GEOSGeom_destroy_r( handle, geos_cut_edges );
     672                 :            :   }
     673                 :            : 
     674                 :          3 :   if ( !GEOSisEmpty_r( handle, collapse_points ) )
     675                 :            :   {
     676                 :          0 :     vgeoms[nvgeoms++] = collapse_points;
     677                 :          0 :   }
     678                 :            :   else
     679                 :            :   {
     680                 :          3 :     GEOSGeom_destroy_r( handle, collapse_points );
     681                 :            :   }
     682                 :            : 
     683                 :          3 :   if ( 1 == nvgeoms )
     684                 :            :   {
     685                 :            :     // Return cut edges
     686                 :          2 :     gout = vgeoms[0];
     687                 :          2 :   }
     688                 :            :   else
     689                 :            :   {
     690                 :            :     // Collect areas and lines (if any line)
     691                 :            :     try
     692                 :            :     {
     693                 :          1 :       gout = GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms );
     694                 :          1 :     }
     695                 :            :     catch ( GEOSException &e )
     696                 :            :     {
     697                 :          0 :       errorMessage = QStringLiteral( "GEOSGeom_createCollection() threw an error: %1" ).arg( e.what() );
     698                 :            :       // TODO: cleanup!
     699                 :          0 :       return nullptr;
     700                 :          0 :     }
     701                 :            :   }
     702                 :            : 
     703                 :          3 :   return gout;
     704                 :            : 
     705                 :          3 : }
     706                 :            : Q_NOWARN_UNREACHABLE_PUSH
     707                 :            : 
     708                 :          3 : static GEOSGeometry *LWGEOM_GEOS_makeValidLine( const GEOSGeometry *gin, QString &errorMessage )
     709                 :            : {
     710                 :          3 :   Q_UNUSED( errorMessage )
     711                 :          3 :   return LWGEOM_GEOS_nodeLines( gin );
     712                 :            : }
     713                 :            : 
     714                 :          1 : static GEOSGeometry *LWGEOM_GEOS_makeValidMultiLine( const GEOSGeometry *gin, QString &errorMessage )
     715                 :            : {
     716                 :          1 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
     717                 :            : 
     718                 :          1 :   int ngeoms = GEOSGetNumGeometries_r( handle, gin );
     719                 :          1 :   uint32_t nlines_alloc = ngeoms;
     720                 :          1 :   QVector<GEOSGeometry *> lines;
     721                 :          1 :   QVector<GEOSGeometry *> points;
     722                 :          1 :   lines.reserve( nlines_alloc );
     723                 :          1 :   points.reserve( ngeoms );
     724                 :            : 
     725                 :          2 :   for ( int i = 0; i < ngeoms; ++i )
     726                 :            :   {
     727                 :          1 :     const GEOSGeometry *g = GEOSGetGeometryN_r( handle, gin, i );
     728                 :          1 :     GEOSGeometry *vg = LWGEOM_GEOS_makeValidLine( g, errorMessage );
     729                 :          1 :     if ( GEOSisEmpty_r( handle, vg ) )
     730                 :            :     {
     731                 :            :       // we don't care about this one
     732                 :          0 :       GEOSGeom_destroy_r( handle, vg );
     733                 :          0 :     }
     734                 :          1 :     if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_POINT )
     735                 :            :     {
     736                 :          1 :       points.append( vg );
     737                 :          1 :     }
     738                 :          0 :     else if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_LINESTRING )
     739                 :            :     {
     740                 :          0 :       lines.append( vg );
     741                 :          0 :     }
     742                 :          0 :     else if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_MULTILINESTRING )
     743                 :            :     {
     744                 :          0 :       int nsubgeoms = GEOSGetNumGeometries_r( handle, vg );
     745                 :          0 :       nlines_alloc += nsubgeoms;
     746                 :          0 :       lines.reserve( nlines_alloc );
     747                 :          0 :       for ( int j = 0; j < nsubgeoms; ++j )
     748                 :            :       {
     749                 :            :         // NOTE: ownership of the cloned geoms will be taken by final collection
     750                 :          0 :         lines.append( GEOSGeom_clone_r( handle, GEOSGetGeometryN_r( handle, vg, j ) ) );
     751                 :          0 :       }
     752                 :          0 :     }
     753                 :            :     else
     754                 :            :     {
     755                 :            :       // NOTE: return from GEOSGeomType will leak but we really don't expect this to happen
     756                 :          0 :       errorMessage = QStringLiteral( "unexpected geom type returned by LWGEOM_GEOS_makeValid: %1" ).arg( GEOSGeomTypeId_r( handle, vg ) );
     757                 :            :     }
     758                 :          1 :   }
     759                 :            : 
     760                 :          1 :   GEOSGeometry *mpoint_out = nullptr;
     761                 :          1 :   if ( !points.isEmpty() )
     762                 :            :   {
     763                 :          1 :     if ( points.count() > 1 )
     764                 :            :     {
     765                 :          0 :       mpoint_out = GEOSGeom_createCollection_r( handle, GEOS_MULTIPOINT, points.data(), points.count() );
     766                 :          0 :     }
     767                 :            :     else
     768                 :            :     {
     769                 :          1 :       mpoint_out = points[0];
     770                 :            :     }
     771                 :          1 :   }
     772                 :            : 
     773                 :          1 :   GEOSGeometry *mline_out = nullptr;
     774                 :          1 :   if ( !lines.isEmpty() )
     775                 :            :   {
     776                 :          0 :     if ( lines.count() > 1 )
     777                 :            :     {
     778                 :          0 :       mline_out = GEOSGeom_createCollection_r( handle, GEOS_MULTILINESTRING, lines.data(), lines.count() );
     779                 :          0 :     }
     780                 :            :     else
     781                 :            :     {
     782                 :          0 :       mline_out = lines[0];
     783                 :            :     }
     784                 :          0 :   }
     785                 :            : 
     786                 :          1 :   if ( mline_out && mpoint_out )
     787                 :            :   {
     788                 :            :     GEOSGeometry *collection[2];
     789                 :          0 :     collection[0] = mline_out;
     790                 :          0 :     collection[1] = mpoint_out;
     791                 :          0 :     return GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, collection, 2 );
     792                 :            :   }
     793                 :          1 :   else if ( mline_out )
     794                 :            :   {
     795                 :          0 :     return mline_out;
     796                 :            :   }
     797                 :          1 :   else if ( mpoint_out )
     798                 :            :   {
     799                 :          1 :     return mpoint_out;
     800                 :            :   }
     801                 :            :   else
     802                 :            :   {
     803                 :          0 :     return nullptr;
     804                 :            :   }
     805                 :          1 : }
     806                 :            : 
     807                 :            : 
     808                 :            : static GEOSGeometry *LWGEOM_GEOS_makeValid( const GEOSGeometry *gin, QString &errorMessage );
     809                 :            : 
     810                 :            : // Will return NULL on error (expect error handler being called by then)
     811                 :          1 : static GEOSGeometry *LWGEOM_GEOS_makeValidCollection( const GEOSGeometry *gin, QString &errorMessage )
     812                 :            : {
     813                 :          1 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
     814                 :            : 
     815                 :          1 :   int nvgeoms = GEOSGetNumGeometries_r( handle, gin );
     816                 :          1 :   if ( nvgeoms == -1 )
     817                 :            :   {
     818                 :          0 :     errorMessage = QStringLiteral( "GEOSGetNumGeometries: %1" ).arg( QLatin1String( "?" ) );
     819                 :          0 :     return nullptr;
     820                 :            :   }
     821                 :            : 
     822                 :          1 :   QVector<GEOSGeometry *> vgeoms( nvgeoms );
     823                 :          4 :   for ( int i = 0; i < nvgeoms; ++i )
     824                 :            :   {
     825                 :          3 :     vgeoms[i] = LWGEOM_GEOS_makeValid( GEOSGetGeometryN_r( handle, gin, i ), errorMessage );
     826                 :          3 :     if ( ! vgeoms[i] )
     827                 :            :     {
     828                 :          0 :       while ( i-- )
     829                 :          0 :         GEOSGeom_destroy_r( handle, vgeoms[i] );
     830                 :            :       // we expect lwerror being called already by makeValid
     831                 :          0 :       return nullptr;
     832                 :            :     }
     833                 :          3 :   }
     834                 :            : 
     835                 :            :   // Collect areas and lines (if any line)
     836                 :            :   try
     837                 :            :   {
     838                 :          1 :     return GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, vgeoms.data(), nvgeoms );
     839                 :          0 :   }
     840                 :            :   catch ( GEOSException &e )
     841                 :            :   {
     842                 :            :     // cleanup and throw
     843                 :          0 :     for ( int i = 0; i < nvgeoms; ++i )
     844                 :          0 :       GEOSGeom_destroy_r( handle, vgeoms[i] );
     845                 :          0 :     errorMessage = QStringLiteral( "GEOSGeom_createCollection() threw an error: %1" ).arg( e.what() );
     846                 :          0 :     return nullptr;
     847                 :          0 :   }
     848                 :          1 : }
     849                 :            : 
     850                 :            : 
     851                 :          9 : static GEOSGeometry *LWGEOM_GEOS_makeValid( const GEOSGeometry *gin, QString &errorMessage )
     852                 :            : {
     853                 :          9 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
     854                 :            : 
     855                 :            :   // return what we got so far if already valid
     856                 :            :   Q_NOWARN_UNREACHABLE_PUSH
     857                 :            :   try
     858                 :            :   {
     859                 :          9 :     if ( GEOSisValid_r( handle, gin ) )
     860                 :            :     {
     861                 :            :       // It's valid at this step, return what we have
     862                 :          2 :       return GEOSGeom_clone_r( handle, gin );
     863                 :            :     }
     864                 :          7 :   }
     865                 :            :   catch ( GEOSException &e )
     866                 :            :   {
     867                 :            :     // I don't think should ever happen
     868                 :          0 :     errorMessage = QStringLiteral( "GEOSisValid(): %1" ).arg( e.what() );
     869                 :          0 :     return nullptr;
     870                 :          0 :   }
     871                 :            :   Q_NOWARN_UNREACHABLE_POP
     872                 :            : 
     873                 :            :   // make what we got valid
     874                 :            : 
     875                 :          7 :   switch ( GEOSGeomTypeId_r( handle, gin ) )
     876                 :            :   {
     877                 :            :     case GEOS_MULTIPOINT:
     878                 :            :     case GEOS_POINT:
     879                 :            :       // points are always valid, but we might have invalid ordinate values
     880                 :          0 :       QgsDebugMsg( QStringLiteral( "PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up" ) );
     881                 :          0 :       return nullptr;
     882                 :            : 
     883                 :            :     case GEOS_LINESTRING:
     884                 :          2 :       return LWGEOM_GEOS_makeValidLine( gin, errorMessage );
     885                 :            : 
     886                 :            :     case GEOS_MULTILINESTRING:
     887                 :          1 :       return LWGEOM_GEOS_makeValidMultiLine( gin, errorMessage );
     888                 :            : 
     889                 :            :     case GEOS_POLYGON:
     890                 :            :     case GEOS_MULTIPOLYGON:
     891                 :          3 :       return LWGEOM_GEOS_makeValidPolygon( gin, errorMessage );
     892                 :            : 
     893                 :            :     case GEOS_GEOMETRYCOLLECTION:
     894                 :          1 :       return LWGEOM_GEOS_makeValidCollection( gin, errorMessage );
     895                 :            : 
     896                 :            :     default:
     897                 :          0 :       errorMessage = QStringLiteral( "ST_MakeValid: doesn't support geometry type: %1" ).arg( GEOSGeomTypeId_r( handle, gin ) );
     898                 :          0 :       return nullptr;
     899                 :            :   }
     900                 :          9 : }
     901                 :            : 
     902                 :            : 
     903                 :          6 : std::unique_ptr< QgsAbstractGeometry > _qgis_lwgeom_make_valid( const QgsAbstractGeometry *lwgeom_in, QString &errorMessage )
     904                 :            : {
     905                 :            :   //bool is3d = FLAGS_GET_Z(lwgeom_in->flags);
     906                 :            : 
     907                 :            :   // try to convert to GEOS, if impossible, clean that up first
     908                 :            :   // otherwise (adding only duplicates of existing points)
     909                 :          6 :   GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();
     910                 :            : 
     911                 :          6 :   geos::unique_ptr geosgeom = QgsGeos::asGeos( lwgeom_in );
     912                 :          6 :   if ( !geosgeom )
     913                 :            :   {
     914                 :          1 :     QgsDebugMsgLevel( QStringLiteral( "Original geom can't be converted to GEOS - will try cleaning that up first" ), 3 );
     915                 :            : 
     916                 :          1 :     std::unique_ptr<QgsAbstractGeometry> lwgeom_in_clone( lwgeom_in->clone() );
     917                 :          1 :     if ( !lwgeom_make_geos_friendly( lwgeom_in_clone.get() ) )
     918                 :            :     {
     919                 :          0 :       QgsDebugMsg( QStringLiteral( "Could not make a valid geometry out of input" ) );
     920                 :          0 :     }
     921                 :            : 
     922                 :            :     // try again as we did cleanup now
     923                 :            :     // TODO: invoke LWGEOM2GEOS directly with autoclean ?
     924                 :          1 :     geosgeom = QgsGeos::asGeos( lwgeom_in_clone.get() );
     925                 :            : 
     926                 :          1 :     if ( ! geosgeom )
     927                 :            :     {
     928                 :          0 :       errorMessage = QStringLiteral( "Could not convert QGIS geom to GEOS" );
     929                 :          0 :       return nullptr;
     930                 :            :     }
     931                 :          1 :   }
     932                 :            :   else
     933                 :            :   {
     934                 :          5 :     QgsDebugMsgLevel( QStringLiteral( "original geom converted to GEOS" ), 4 );
     935                 :            :   }
     936                 :            : 
     937                 :          6 :   GEOSGeometry *geosout = LWGEOM_GEOS_makeValid( geosgeom.get(), errorMessage );
     938                 :          6 :   if ( !geosout )
     939                 :          0 :     return nullptr;
     940                 :            : 
     941                 :          6 :   std::unique_ptr< QgsAbstractGeometry > lwgeom_out = QgsGeos::fromGeos( geosout );
     942                 :          6 :   GEOSGeom_destroy_r( handle, geosout );
     943                 :          6 :   if ( !lwgeom_out )
     944                 :          0 :     return nullptr;
     945                 :            : 
     946                 :            :   // force multi-type if we had a multi-type before
     947                 :          6 :   if ( QgsWkbTypes::isMultiType( lwgeom_in->wkbType() ) && !QgsWkbTypes::isMultiType( lwgeom_out->wkbType() ) )
     948                 :            :   {
     949                 :          1 :     QgsGeometryCollection *collection = nullptr;
     950                 :          1 :     switch ( QgsWkbTypes::multiType( lwgeom_out->wkbType() ) )
     951                 :            :     {
     952                 :            :       case QgsWkbTypes::MultiPoint:
     953                 :          1 :         collection = new QgsMultiPoint();
     954                 :          1 :         break;
     955                 :            :       case QgsWkbTypes::MultiLineString:
     956                 :          0 :         collection = new QgsMultiLineString();
     957                 :          0 :         break;
     958                 :            :       case QgsWkbTypes::MultiPolygon:
     959                 :          0 :         collection = new QgsMultiPolygon();
     960                 :          0 :         break;
     961                 :            :       default:
     962                 :          0 :         collection = new QgsGeometryCollection();
     963                 :          0 :         break;
     964                 :            :     }
     965                 :          1 :     collection->addGeometry( lwgeom_out.release() ); // takes ownership
     966                 :          1 :     lwgeom_out.reset( collection );
     967                 :          1 :   }
     968                 :            : 
     969                 :          6 :   return lwgeom_out;
     970                 :          6 : }

Generated by: LCOV version 1.14