Example #1
0
bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum )
{
  if ( !geom || partNum < 0 )
  {
    return false;
  }

  if ( ringNum < 1 ) //cannot remove exterior ring
  {
    return false;
  }

  QgsAbstractGeometry *g = geom;
  QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom );
  if ( c )
  {
    g = c->geometryN( partNum );
  }
  else if ( partNum > 0 )
  {
    //part num specified, but not a multi part geometry type
    return false;
  }

  QgsCurvePolygon *cpoly = qgsgeometry_cast<QgsCurvePolygon *>( g );
  if ( !cpoly )
  {
    return false;
  }

  return cpoly->removeInteriorRing( ringNum - 1 );
}
Example #2
0
static bool lwcollection_make_geos_friendly( QgsGeometryCollection &g )
{
  for ( int i = 0; i < g.numGeometries(); i++ )
  {
    if ( !lwgeom_make_geos_friendly( *g.geometryN( i ) ) )
      return false;
  }

  return true;
}
Example #3
0
bool QgsGeometryEditUtils::deletePart( QgsAbstractGeometry *geom, int partNum )
{
  if ( !geom )
  {
    return false;
  }

  QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom );
  if ( !c )
  {
    return false;
  }

  return c->removeGeometry( partNum );
}
QgsAbstractGeometry *QgsGeometryCollection::segmentize( double tolerance, SegmentationToleranceType toleranceType ) const
{
  std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkbType( mWkbType ) );
  QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
  if ( !geomCollection )
  {
    return clone();
  }

  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
  {
    geomCollection->addGeometry( ( *geomIt )->segmentize( tolerance, toleranceType ) );
  }
  return geom.release();
}
void QgsGeometryTypeCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
  QgsFeature feature;
  if ( !mFeaturePool->get( error->featureId(), feature ) )
  {
    error->setObsolete();
    return;
  }
  QgsGeometry featureGeom = feature.geometry();
  QgsAbstractGeometry* geom = featureGeom.geometry();

  // Check if error still applies
  QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom->wkbType() );
  if (( mAllowedTypes & ( 1 << type ) ) != 0 )
  {
    error->setObsolete();
    return;
  }

  // Fix with selected method
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else if ( method == Convert )
  {
    // Check if corresponding single type is allowed
    if ( QgsWkbTypes::isMultiType( type ) && (( 1 << QgsWkbTypes::singleType( type ) ) & mAllowedTypes ) != 0 )
    {
      // Explode multi-type feature into single-type features
      for ( int iPart = 1, nParts = geom->partCount(); iPart < nParts; ++iPart )
      {
        QgsFeature newFeature;
        newFeature.setAttributes( feature.attributes() );
        newFeature.setGeometry( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, iPart )->clone() ) );
        mFeaturePool->addFeature( newFeature );
        changes[newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) );
      }
      // Recycle feature for part 0
      feature.setGeometry( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, 0 )->clone() ) );
      mFeaturePool->updateFeature( feature );
      changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
    }
    // Check if corresponding multi type is allowed
    else if ( QgsWkbTypes::isSingleType( type ) && (( 1 << QgsWkbTypes::multiType( type ) ) & mAllowedTypes ) != 0 )
    {
      QgsGeometryCollection* geomCollection = nullptr;
      switch ( QgsWkbTypes::multiType( type ) )
      {
        case QgsWkbTypes::MultiPoint:
        {
          geomCollection = new QgsMultiPointV2();
          break;
        }
        case QgsWkbTypes::MultiLineString:
        {
          geomCollection = new QgsMultiLineString();
          break;
        }
        case QgsWkbTypes::MultiPolygon:
        {
          geomCollection = new QgsMultiPolygonV2();
          break;
        }
        case QgsWkbTypes::MultiCurve:
        {
          geomCollection = new QgsMultiCurve();
          break;
        }
        case QgsWkbTypes::MultiSurface:
        {
          geomCollection = new QgsMultiSurface();
          break;
        }
        default:
          break;
      }
      if ( !geomCollection )
      {
        error->setFixFailed( tr( "Unknown geometry type" ) );
      }
      else
      {
        geomCollection->addGeometry( geom->clone() );

        feature.setGeometry( QgsGeometry( geomCollection ) );
        mFeaturePool->updateFeature( feature );
        changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
      }
    }
    // Delete feature
    else
    {
      mFeaturePool->deleteFeature( feature );
      changes[error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) );
    }
    error->setFixed( method );
  }
  else if ( method == Delete )
  {
    mFeaturePool->deleteFeature( feature );
    error->setFixed( method );
    changes[error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) );
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}
Example #6
0
QgsGeometry::OperationResult QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, std::unique_ptr<QgsAbstractGeometry> part )
{
  if ( !geom )
  {
    return QgsGeometry::InvalidBaseGeometry;
  }

  if ( !part )
  {
    return QgsGeometry::InvalidInput;
  }

  //multitype?
  QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom );
  if ( !geomCollection )
  {
    return QgsGeometry::AddPartNotMultiGeometry;
  }

  bool added = false;
  if ( QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiSurface
       || QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiPolygon )
  {
    QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() );

    if ( curve && curve->isClosed() && curve->numPoints() >= 4 )
    {
      std::unique_ptr<QgsCurvePolygon> poly;
      if ( QgsWkbTypes::flatType( curve->wkbType() ) == QgsWkbTypes::LineString )
      {
        poly = qgis::make_unique< QgsPolygonV2 >();
      }
      else
      {
        poly = qgis::make_unique< QgsCurvePolygon >();
      }
      // Ownership is still with part, curve points to the same object and is transferred
      // to poly here.
      part.release();
      poly->setExteriorRing( curve );
      added = geomCollection->addGeometry( poly.release() );
    }
    else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Polygon )
    {
      added = geomCollection->addGeometry( part.release() );
    }
    else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiPolygon )
    {
      std::unique_ptr<QgsGeometryCollection> parts( static_cast<QgsGeometryCollection *>( part.release() ) );

      int i;
      int n = geomCollection->numGeometries();
      for ( i = 0; i < parts->numGeometries() && geomCollection->addGeometry( parts->geometryN( i )->clone() ); i++ )
        ;

      added = i == parts->numGeometries();
      if ( !added )
      {
        while ( geomCollection->numGeometries() > n )
          geomCollection->removeGeometry( n );
        return QgsGeometry::InvalidInput;
      }
    }
    else
    {
      return QgsGeometry::InvalidInput;
    }
  }
  else
  {
    added = geomCollection->addGeometry( part.release() );
  }
  return added ? QgsGeometry::Success : QgsGeometry::InvalidInput;
}
Example #7
0
QgsGeometry::OperationResult QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, std::unique_ptr<QgsCurve> ring )
{
  if ( !ring )
  {
    return QgsGeometry::InvalidInput;
  }

  QList< QgsCurvePolygon * > polygonList;
  QgsCurvePolygon *curvePoly = qgsgeometry_cast< QgsCurvePolygon * >( geom );
  QgsGeometryCollection *multiGeom = qgsgeometry_cast< QgsGeometryCollection * >( geom );
  if ( curvePoly )
  {
    polygonList.append( curvePoly );
  }
  else if ( multiGeom )
  {
    polygonList.reserve( multiGeom->numGeometries() );
    for ( int i = 0; i < multiGeom->numGeometries(); ++i )
    {
      polygonList.append( qgsgeometry_cast< QgsCurvePolygon * >( multiGeom->geometryN( i ) ) );
    }
  }
  else
  {
    return QgsGeometry::InvalidInput; //not polygon / multipolygon;
  }

  //ring must be closed
  if ( !ring->isClosed() )
  {
    return QgsGeometry::AddRingNotClosed;
  }
  else if ( !ring->isRing() )
  {
    return QgsGeometry::AddRingNotValid;
  }

  std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring.get() ) );
  ringGeom->prepareGeometry();

  //for each polygon, test if inside outer ring and no intersection with other interior ring
  QList< QgsCurvePolygon * >::const_iterator polyIter = polygonList.constBegin();
  for ( ; polyIter != polygonList.constEnd(); ++polyIter )
  {
    if ( ringGeom->within( *polyIter ) )
    {
      //check if disjoint with other interior rings
      int nInnerRings = ( *polyIter )->numInteriorRings();
      for ( int i = 0; i < nInnerRings; ++i )
      {
        if ( !ringGeom->disjoint( ( *polyIter )->interiorRing( i ) ) )
        {
          return QgsGeometry::AddRingCrossesExistingRings;
        }
      }

      //make sure dimensionality of ring matches geometry
      if ( QgsWkbTypes::hasZ( geom->wkbType() ) )
        ring->addZValue( 0 );
      if ( QgsWkbTypes::hasM( geom->wkbType() ) )
        ring->addMValue( 0 );

      ( *polyIter )->addInteriorRing( ring.release() );
      return QgsGeometry::Success; //success
    }
  }
  return QgsGeometry::AddRingNotInExistingFeature; //not contained in any outer ring
}
std::unique_ptr< QgsAbstractGeometry > _qgis_lwgeom_make_valid( const QgsAbstractGeometry *lwgeom_in, QString &errorMessage )
{
  //bool is3d = FLAGS_GET_Z(lwgeom_in->flags);

  // try to convert to GEOS, if impossible, clean that up first
  // otherwise (adding only duplicates of existing points)
  GEOSContextHandle_t handle = QgsGeos::getGEOSHandler();

  geos::unique_ptr geosgeom = QgsGeos::asGeos( lwgeom_in );
  if ( !geosgeom )
  {
    QgsDebugMsg( QStringLiteral( "Original geom can't be converted to GEOS - will try cleaning that up first" ) );

    std::unique_ptr<QgsAbstractGeometry> lwgeom_in_clone( lwgeom_in->clone() );
    if ( !lwgeom_make_geos_friendly( lwgeom_in_clone.get() ) )
    {
      QgsDebugMsg( QStringLiteral( "Could not make a valid geometry out of input" ) );
    }

    // try again as we did cleanup now
    // TODO: invoke LWGEOM2GEOS directly with autoclean ?
    geosgeom = QgsGeos::asGeos( lwgeom_in_clone.get() );

    if ( ! geosgeom )
    {
      errorMessage = QStringLiteral( "Could not convert QGIS geom to GEOS" );
      return nullptr;
    }
  }
  else
  {
    QgsDebugMsgLevel( QStringLiteral( "original geom converted to GEOS" ), 4 );
  }

  GEOSGeometry *geosout = LWGEOM_GEOS_makeValid( geosgeom.get(), errorMessage );
  if ( !geosout )
    return nullptr;

  std::unique_ptr< QgsAbstractGeometry > lwgeom_out = QgsGeos::fromGeos( geosout );
  GEOSGeom_destroy_r( handle, geosout );
  if ( !lwgeom_out )
    return nullptr;

  // force multi-type if we had a multi-type before
  if ( QgsWkbTypes::isMultiType( lwgeom_in->wkbType() ) && !QgsWkbTypes::isMultiType( lwgeom_out->wkbType() ) )
  {
    QgsGeometryCollection *collection = nullptr;
    switch ( QgsWkbTypes::multiType( lwgeom_out->wkbType() ) )
    {
      case QgsWkbTypes::MultiPoint:
        collection = new QgsMultiPoint();
        break;
      case QgsWkbTypes::MultiLineString:
        collection = new QgsMultiLineString();
        break;
      case QgsWkbTypes::MultiPolygon:
        collection = new QgsMultiPolygon();
        break;
      default:
        collection = new QgsGeometryCollection();
        break;
    }
    collection->addGeometry( lwgeom_out.release() ); // takes ownership
    lwgeom_out.reset( collection );
  }

  return lwgeom_out;
}
void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
  QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ];
  QgsFeature feature;
  if ( !featurePool->getFeature( error->featureId(), feature ) )
  {
    error->setObsolete();
    return;
  }
  QgsGeometry featureGeom = feature.geometry();
  QgsAbstractGeometry *geom = featureGeom.get();
  QgsVertexId vidx = error->vidx();

  // Check if ring still exists
  if ( !vidx.isValid( geom ) )
  {
    error->setObsolete();
    return;
  }

  const QgsGeometryUtils::SelfIntersection &inter = static_cast<QgsGeometrySelfIntersectionCheckError *>( error )->intersection();
  // Check if error still applies
  bool ringIsClosed = false;
  int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, vidx.part, vidx.ring, &ringIsClosed );
  if ( nVerts == 0 || inter.segment1 >= nVerts || inter.segment2 >= nVerts )
  {
    error->setObsolete();
    return;
  }
  QgsPoint p1 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, inter.segment1 ) );
  QgsPoint q1 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, inter.segment2 ) );
  QgsPoint p2 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( inter.segment1 + 1 ) % nVerts ) );
  QgsPoint q2 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( inter.segment2 + 1 ) % nVerts ) );
  QgsPoint s;
  bool intersection = false;
  if ( !QgsGeometryUtils::segmentIntersection( p1, p2, q1, q2, s, intersection, mContext->tolerance ) )
  {
    error->setObsolete();
    return;
  }

  // Fix with selected method
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else if ( method == ToMultiObject || method == ToSingleObjects )
  {
    // Extract rings
    QgsPointSequence ring1, ring2;
    bool ring1EndsWithS = false;
    bool ring2EndsWithS = false;
    for ( int i = 0; i < nVerts; ++i )
    {
      if ( i <= inter.segment1 || i >= inter.segment2 + 1 )
      {
        ring1.append( geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, i ) ) );
        ring1EndsWithS = false;
        if ( i == inter.segment2 + 1 )
        {
          ring2.append( s );
          ring2EndsWithS = true;
        }
      }
      else
      {
        ring2.append( geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, i ) ) );
        ring2EndsWithS = true;
        if ( i == inter.segment1 + 1 )
        {
          ring1.append( s );
          ring1EndsWithS = false;
        }
      }
    }
    if ( nVerts == inter.segment2 + 1 )
    {
      ring2.append( s );
      ring2EndsWithS = true;
    }
    if ( ringIsClosed || ring1EndsWithS )
      ring1.append( ring1.front() ); // Ensure ring is closed
    if ( ringIsClosed || ring2EndsWithS )
      ring2.append( ring2.front() ); // Ensure ring is closed

    if ( ring1.size() < 3 + ( ringIsClosed || ring1EndsWithS ) || ring2.size() < 3 + ( ringIsClosed || ring2EndsWithS ) )
    {
      error->setFixFailed( tr( "Resulting geometry is degenerate" ) );
      return;
    }
    QgsLineString *ringGeom1 = new QgsLineString();
    ringGeom1->setPoints( ring1 );
    QgsLineString *ringGeom2 = new QgsLineString();
    ringGeom2->setPoints( ring2 );

    QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( geom, vidx.part );
    // If is a polygon...
    if ( dynamic_cast<QgsCurvePolygon *>( part ) )
    {
      QgsCurvePolygon *poly = static_cast<QgsCurvePolygon *>( part );
      // If self-intersecting ring is an interior ring, create separate holes
      if ( vidx.ring > 0 )
      {
        poly->removeInteriorRing( vidx.ring );
        poly->addInteriorRing( ringGeom1 );
        poly->addInteriorRing( ringGeom2 );
        changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeRemoved, vidx ) );
        changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeAdded, QgsVertexId( vidx.part, poly->ringCount() - 2 ) ) );
        changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeAdded, QgsVertexId( vidx.part, poly->ringCount() - 1 ) ) );
        feature.setGeometry( featureGeom );
        featurePool->updateFeature( feature );
      }
      else
      {
        // If ring is exterior, build two polygons, and reassign interiors as necessary
        poly->setExteriorRing( ringGeom1 );

        // If original feature was a linear polygon, also create the new part as a linear polygon
        QgsCurvePolygon *poly2 = dynamic_cast<QgsPolygon *>( part ) ? new QgsPolygon() : new QgsCurvePolygon();
        poly2->setExteriorRing( ringGeom2 );

        // Reassing interiors as necessary
        std::unique_ptr< QgsGeometryEngine > geomEnginePoly1 = QgsGeometryCheckerUtils::createGeomEngine( poly, mContext->tolerance );
        std::unique_ptr< QgsGeometryEngine > geomEnginePoly2 = QgsGeometryCheckerUtils::createGeomEngine( poly2, mContext->tolerance );
        for ( int n = poly->numInteriorRings(), i = n - 1; i >= 0; --i )
        {
          if ( !geomEnginePoly1->contains( poly->interiorRing( i ) ) )
          {
            if ( geomEnginePoly2->contains( poly->interiorRing( i ) ) )
            {
              poly2->addInteriorRing( static_cast<QgsCurve *>( poly->interiorRing( i )->clone() ) );
              // No point in adding ChangeAdded changes, since the entire poly2 is added anyways later on
            }
            poly->removeInteriorRing( i );
            changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeRemoved, QgsVertexId( vidx.part, 1 + i ) ) );
          }
        }

        if ( method == ToMultiObject )
        {
          // If is already a geometry collection, just add the new polygon.
          if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
          {
            static_cast<QgsGeometryCollection *>( geom )->addGeometry( poly2 );
            changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeChanged, QgsVertexId( vidx.part, vidx.ring ) ) );
            changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geom->partCount() - 1 ) ) );
            feature.setGeometry( featureGeom );
            featurePool->updateFeature( feature );
          }
          // Otherwise, create multipolygon
          else
          {
            QgsMultiPolygon *multiPoly = new QgsMultiPolygon();
            multiPoly->addGeometry( poly->clone() );
            multiPoly->addGeometry( poly2 );
            feature.setGeometry( QgsGeometry( multiPoly ) );
            featurePool->updateFeature( feature );
            changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
          }
        }
        else // if ( method == ToSingleObjects )
        {
          QgsFeature newFeature;
          newFeature.setAttributes( feature.attributes() );
          newFeature.setGeometry( QgsGeometry( poly2 ) );
          feature.setGeometry( featureGeom );
          featurePool->updateFeature( feature );
          featurePool->addFeature( newFeature );
          changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeChanged, QgsVertexId( vidx.part, vidx.ring ) ) );
          changes[error->layerId()][newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) );
        }
      }
    }
    else if ( dynamic_cast<QgsCurve *>( part ) )
    {
      if ( method == ToMultiObject )
      {
        if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
        {
          QgsGeometryCollection *geomCollection = static_cast<QgsGeometryCollection *>( geom );
          geomCollection->removeGeometry( vidx.part );
          geomCollection->addGeometry( ringGeom1 );
          geomCollection->addGeometry( ringGeom2 );
          feature.setGeometry( featureGeom );
          featurePool->updateFeature( feature );
          changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeRemoved, QgsVertexId( vidx.part ) ) );
          changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 2 ) ) );
          changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 1 ) ) );
        }
        else
        {
          QgsMultiCurve *geomCollection = new QgsMultiLineString();
          geomCollection->addGeometry( ringGeom1 );
          geomCollection->addGeometry( ringGeom2 );
          feature.setGeometry( QgsGeometry( geomCollection ) );
          featurePool->updateFeature( feature );
          changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
        }
      }
      else // if(method == ToSingleObjects)
      {
        if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
        {
          QgsGeometryCollection *geomCollection = static_cast<QgsGeometryCollection *>( geom );
          geomCollection->removeGeometry( vidx.part );
          geomCollection->addGeometry( ringGeom1 );
          feature.setGeometry( featureGeom );
          featurePool->updateFeature( feature );
          changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeRemoved, QgsVertexId( vidx.part ) ) );
          changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 1 ) ) );
        }
        else
        {
          feature.setGeometry( QgsGeometry( ringGeom1 ) );
          featurePool->updateFeature( feature );
          changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged, QgsVertexId( vidx.part ) ) );
        }
        QgsFeature newFeature;
        newFeature.setAttributes( feature.attributes() );
        newFeature.setGeometry( QgsGeometry( ringGeom2 ) );
        featurePool->addFeature( newFeature );
        changes[error->layerId()][newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) );
      }
    }
    else
    {
      delete ringGeom1;
      delete ringGeom2;
    }
    error->setFixed( method );
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}