Example #1
0
void QgsSpatialQuery::populateIndexResultDisjoint(
  QgsFeatureIds &qsetIndexResult, QgsFeatureId idTarget, const QgsGeometry& geomTarget,
  bool ( QgsGeometryEngine::* op )( const QgsAbstractGeometry&, QString* ) const )
{
  QgsFeatureIds listIdReference = mIndexReference.intersects( geomTarget.boundingBox() ).toSet();
  if ( listIdReference.isEmpty() )
  {
    qsetIndexResult.insert( idTarget );
    return;
  }

  //prepare geometry
  QgsGeometryEngine* geomEngine = geomTarget.createGeometryEngine( geomTarget.geometry() );
  geomEngine->prepareGeometry();

  QgsFeature featureReference;
  QgsGeometry geomReference;
  QgsFeatureIterator listIt = mLayerReference->getFeatures( QgsFeatureRequest().setFilterFids( listIdReference ) );

  bool addIndex = true;
  while ( listIt.nextFeature( featureReference ) )
  {
    geomReference = featureReference.geometry();
    if (( geomEngine->*op )( *geomReference.geometry(), 0 ) )
    {
      addIndex = false;
      break;
    }
  }
  if ( addIndex )
  {
    qsetIndexResult.insert( idTarget );
  }
  delete geomEngine;
} // void QgsSpatialQuery::populateIndexResultDisjoint( ...
Example #2
0
QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertexV2( QgsFeatureId featureId, int vertex )
{
    if ( !L->hasGeometryType() )
        return QgsVectorLayer::InvalidLayer;

    QgsGeometry geometry;
    if ( !cache()->geometry( featureId, geometry ) )
    {
        // it's not in cache: let's fetch it from layer
        QgsFeature f;
        if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
            return QgsVectorLayer::FetchFeatureFailed; // geometry not found

        geometry = f.geometry();
    }

    if ( !geometry.deleteVertex( vertex ) )
        return QgsVectorLayer::EditFailed;

    if ( geometry.geometry() && geometry.geometry()->nCoordinates() == 0 )
    {
        //last vertex deleted, set geometry to null
        geometry.setGeometry( nullptr );
    }

    L->editBuffer()->changeGeometry( featureId, geometry );
    return !geometry.isEmpty() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry;
}
Example #3
0
bool QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId atFeatureId, int atVertex )
{
  if ( !L->hasGeometryType() )
    return false;

  QgsGeometry geometry;
  if ( !cache()->geometry( atFeatureId, geometry ) )
  {
    // it's not in cache: let's fetch it from layer
    QgsFeature f;
    if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.constGeometry() )
      return false; // geometry not found

    geometry = *f.constGeometry();
  }

  if ( !geometry.deleteVertex( atVertex ) )
    return false;

  if ( geometry.geometry() && geometry.geometry()->nCoordinates() == 0 )
  {
    //last vertex deleted, set geometry to null
    geometry.setGeometry( 0 );
  }


  L->editBuffer()->changeGeometry( atFeatureId, &geometry );
  return true;
}
void QgsGeometryHoleCheck::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();
  QgsVertexId vidx = error->vidx();

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

  // Fix error
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else if ( method == RemoveHoles )
  {
    deleteFeatureGeometryRing( feature, vidx.part, vidx.ring, changes );
    error->setFixed( method );
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}
void QgsGeometryDuplicateNodesCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
  const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
  Q_FOREACH ( QgsFeatureId featureid, featureIds )
  {
    if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
    QgsFeature feature;
    if ( !mFeaturePool->get( featureid, feature ) )
    {
      continue;
    }

    QgsGeometry featureGeom = feature.geometry();
    QgsAbstractGeometry* geom = featureGeom.geometry();
    for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
    {
      for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
      {
        int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing );
        if ( nVerts < 2 )
          continue;
        for ( int iVert = nVerts - 1, jVert = 0; jVert < nVerts; iVert = jVert++ )
        {
          QgsPointV2 pi = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) );
          QgsPointV2 pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) );
          if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance() )
          {
            errors.append( new QgsGeometryCheckError( this, featureid, pj, QgsVertexId( iPart, iRing, jVert ) ) );
          }
        }
      }
    }
  }
}
void QgsGeometrySelfIntersectionCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
{
  const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
  Q_FOREACH ( QgsFeatureId featureid, featureIds )
  {
    if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
    QgsFeature feature;
    if ( !mFeaturePool->get( featureid, feature ) )
    {
      continue;
    }
    QgsGeometry featureGeom = feature.geometry();
    QgsAbstractGeometry *geom = featureGeom.geometry();

    for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
    {
      for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
      {
        Q_FOREACH ( const QgsGeometryUtils::SelfIntersection &inter, QgsGeometryUtils::getSelfIntersections( geom, iPart, iRing, QgsGeometryCheckPrecision::tolerance() ) )
        {
          errors.append( new QgsGeometrySelfIntersectionCheckError( this, featureid, inter.point, QgsVertexId( iPart, iRing ), inter ) );
        }
      }
    }
  }
Example #7
0
double QgsDistanceArea::measureArea( const QgsGeometry& geometry ) const
{
  if ( geometry.isEmpty() )
    return 0.0;

  const QgsAbstractGeometry* geomV2 = geometry.geometry();
  return measure( geomV2, Area );
}
Example #8
0
double QgsDistanceArea::measureLength( const QgsGeometry& geometry ) const
{
  if ( geometry.isNull() )
    return 0.0;

  const QgsAbstractGeometry* geomV2 = geometry.geometry();
  return measure( geomV2, Length );
}
void QgsGeometryDuplicateNodesCheck::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();
  QgsVertexId vidx = error->vidx();

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

  // Check if error still applies
  int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, vidx.part, vidx.ring );
  QgsPointV2 pi = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + nVerts - 1 ) % nVerts ) );
  QgsPointV2 pj = geom->vertexAt( error->vidx() );
  if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) >= QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance() )
  {
    error->setObsolete();
    return;
  }

  // Fix error
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else if ( method == RemoveDuplicates )
  {
    if ( !QgsGeometryCheckerUtils::canDeleteVertex( geom, vidx.part, vidx.ring ) )
    {
      error->setFixFailed( tr( "Resulting geometry is degenerate" ) );
    }
    else if ( !geom->deleteVertex( error->vidx() ) )
    {
      error->setFixFailed( tr( "Failed to delete vertex" ) );
    }
    else
    {
      feature.setGeometry( featureGeom );
      mFeaturePool->updateFeature( feature );
      error->setFixed( method );
      changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, error->vidx() ) );
    }
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}
Example #10
0
double QgsDistanceArea::measurePerimeter( const QgsGeometry& geometry ) const
{
  if ( geometry.isNull() )
    return 0.0;

  const QgsAbstractGeometry* geomV2 = geometry.geometry();
  if ( !geomV2 || geomV2->dimension() < 2 )
  {
    return 0.0;
  }

  if ( !mEllipsoidalMode || mEllipsoid == GEO_NONE )
  {
    return geomV2->perimeter();
  }

  //create list with (single) surfaces
  QList< const QgsSurface* > surfaces;
  const QgsSurface* surf = dynamic_cast<const QgsSurface*>( geomV2 );
  if ( surf )
  {
    surfaces.append( surf );
  }
  const QgsMultiSurface* multiSurf = dynamic_cast<const QgsMultiSurface*>( geomV2 );
  if ( multiSurf )
  {
    surfaces.reserve(( surf ? 1 : 0 ) + multiSurf->numGeometries() );
    for ( int i = 0; i  < multiSurf->numGeometries(); ++i )
    {
      surfaces.append( static_cast<const QgsSurface*>( multiSurf->geometryN( i ) ) );
    }
  }

  double length = 0;
  QList<const QgsSurface*>::const_iterator surfaceIt = surfaces.constBegin();
  for ( ; surfaceIt != surfaces.constEnd(); ++surfaceIt )
  {
    if ( !*surfaceIt )
    {
      continue;
    }

    QgsPolygonV2* poly = ( *surfaceIt )->surfaceToPolygon();
    const QgsCurve* outerRing = poly->exteriorRing();
    if ( outerRing )
    {
      length += measure( outerRing );
    }
    int nInnerRings = poly->numInteriorRings();
    for ( int i = 0; i < nInnerRings; ++i )
    {
      length += measure( poly->interiorRing( i ) );
    }
    delete poly;
  }
  return length;
}
Example #11
0
void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
{
  const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
  Q_FOREACH ( QgsFeatureId featureid, featureIds )
  {
    if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
    QgsFeature feature;
    if ( !mFeaturePool->get( featureid, feature ) )
    {
      continue;
    }
    QgsGeometry featureGeom = feature.geometry();
    QgsAbstractGeometry *geom = featureGeom.geometry();
    QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::tolerance() );

    QgsFeatureIds ids = mFeaturePool->getIntersects( feature.geometry().boundingBox() );
    Q_FOREACH ( QgsFeatureId otherid, ids )
    {
      // >= : only report overlaps once
      if ( otherid >= featureid )
      {
        continue;
      }

      QgsFeature otherFeature;
      if ( !mFeaturePool->get( otherid, otherFeature ) )
      {
        continue;
      }

      QString errMsg;
      if ( geomEngine->overlaps( *otherFeature.geometry().geometry(), &errMsg ) )
      {
        QgsAbstractGeometry *interGeom = geomEngine->intersection( *otherFeature.geometry().geometry() );
        if ( interGeom && !interGeom->isEmpty() )
        {
          QgsGeometryCheckerUtils::filter1DTypes( interGeom );
          for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart )
          {
            double area = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->area();
            if ( area > QgsGeometryCheckPrecision::reducedTolerance() && area < mThreshold )
            {
              errors.append( new QgsGeometryOverlapCheckError( this, featureid, QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->centroid(), area, otherid ) );
            }
          }
        }
        else if ( !errMsg.isEmpty() )
        {
          messages.append( tr( "Overlap check between features %1 and %2: %3" ).arg( feature.id() ).arg( otherFeature.id() ).arg( errMsg ) );
        }
        delete interGeom;
      }
    }
    delete geomEngine;
  }
}
Example #12
0
void QgsGeometryContainedCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
    const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
    Q_FOREACH ( QgsFeatureId featureid, featureIds )
    {
        if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
        QgsFeature feature;
        if ( !mFeaturePool->get( featureid, feature ) )
        {
            continue;
        }

        QgsGeometry featureGeom = feature.geometry();
        QgsGeometryEngine* geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeom.geometry(), QgsGeometryCheckPrecision::tolerance() );

        QgsFeatureIds ids = mFeaturePool->getIntersects( featureGeom.geometry()->boundingBox() );
        Q_FOREACH ( QgsFeatureId otherid, ids )
        {
            if ( otherid == featureid )
            {
                continue;
            }
            QgsFeature otherFeature;
            if ( !mFeaturePool->get( otherid, otherFeature ) )
            {
                continue;
            }

            QString errMsg;
            if ( geomEngine->within( *otherFeature.geometry().geometry(), &errMsg ) )
            {
                errors.append( new QgsGeometryContainedCheckError( this, featureid, feature.geometry().geometry()->centroid(), otherid ) );
            }
            else if ( !errMsg.isEmpty() )
            {
                messages.append( tr( "Feature %1 within feature %2: %3" ).arg( feature.id() ).arg( otherFeature.id() ).arg( errMsg ) );
            }
        }
        delete geomEngine;
    }
}
Example #13
0
void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
{
  const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
  Q_FOREACH ( QgsFeatureId featureid, featureIds )
  {
    if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
    QgsFeature feature;
    if ( !mFeaturePool->get( featureid, feature ) )
    {
      continue;
    }
    QgsGeometry g = feature.geometry();
    const QgsAbstractGeometry *geom = g.geometry();
    for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
    {
      for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
      {
        int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing );
        // Less than three points, no angles to check
        if ( nVerts < 3 )
        {
          continue;
        }
        for ( int iVert = 0; iVert < nVerts; ++iVert )
        {
          const QgsPoint &p1 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert - 1 + nVerts ) % nVerts ) );
          const QgsPoint &p2 = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) );
          const QgsPoint &p3 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert + 1 ) % nVerts ) );
          QgsVector v21, v23;
          try
          {
            v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normalized();
            v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normalized();
          }
          catch ( const QgsException & )
          {
            // Zero length vectors
            continue;
          }

          double angle = std::acos( v21 * v23 ) / M_PI * 180.0;
          if ( angle < mMinAngle )
          {
            errors.append( new QgsGeometryCheckError( this, featureid, p2, QgsVertexId( iPart, iRing, iVert ), angle ) );
          }
        }
      }
    }
  }
}
Example #14
0
//! Returns a simplified version the specified geometry (Removing duplicated points) when is applied the specified map2pixel context
QgsGeometry QgsMapToPixelSimplifier::simplify( const QgsGeometry& geometry ) const
{
  if ( geometry.isEmpty() )
  {
    return QgsGeometry();
  }
  if ( mSimplifyFlags == QgsMapToPixelSimplifier::NoFlags )
  {
    return geometry;
  }

  // Check whether the geometry can be simplified using the map2pixel context
  const QgsWkbTypes::Type singleType = QgsWkbTypes::singleType( geometry.wkbType() );
  const QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( singleType );
  if ( flatType == QgsWkbTypes::Point )
  {
    return geometry;
  }

  const bool isaLinearRing = flatType == QgsWkbTypes::Polygon;
  const int numPoints = geometry.geometry()->nCoordinates();

  if ( numPoints <= ( isaLinearRing ? 6 : 3 ) )
  {
    // No simplify simple geometries
    return geometry;
  }

  const QgsRectangle envelope = geometry.boundingBox();
  if ( qMax( envelope.width(), envelope.height() ) / numPoints > mTolerance * 2.0 )
  {
    //points are in average too far apart to lead to any significant simplification
    return geometry;
  }

  return simplifyGeometry( mSimplifyFlags, mSimplifyAlgorithm, geometry.wkbType(), *geometry.geometry(), envelope, mTolerance, false );
}
Example #15
0
void QgsSelectByFormDialog::zoomToFeatures( const QString &filter )
{
  QgsFeatureIds ids;

  QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer ) );

  QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( filter )
                              .setExpressionContext( context )
                              .setSubsetOfAttributes( QgsAttributeList() );

  QgsFeatureIterator features = mLayer->getFeatures( request );

  QgsRectangle bbox;
  bbox.setMinimal();
  QgsFeature feat;
  int featureCount = 0;
  while ( features.nextFeature( feat ) )
  {
    QgsGeometry geom = feat.geometry();
    if ( geom.isNull() || geom.geometry()->isEmpty() )
      continue;

    QgsRectangle r = mMapCanvas->mapSettings().layerExtentToOutputExtent( mLayer, geom.boundingBox() );
    bbox.combineExtentWith( r );
    featureCount++;
  }
  features.close();

  QgsSettings settings;
  int timeout = settings.value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
  if ( featureCount > 0 )
  {
    mMapCanvas->zoomToFeatureExtent( bbox );
    if ( mMessageBar )
    {
      mMessageBar->pushMessage( QString(),
                                tr( "Zoomed to %n matching feature(s)", "number of matching features", featureCount ),
                                QgsMessageBar::INFO,
                                timeout );
    }
  }
  else if ( mMessageBar )
  {
    mMessageBar->pushMessage( QString(),
                              tr( "No matching features found" ),
                              QgsMessageBar::INFO,
                              timeout );
  }
}
Example #16
0
void QgsGeometryMultipartCheck::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
  if ( geom->partCount() > 1 || !QgsWkbTypes::isMultiType( geom->wkbType() ) )
  {
    error->setObsolete();
    return;
  }

  // Fix error
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else if ( method == ConvertToSingle )
  {
    feature.setGeometry( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, 0 )->clone() ) );
    mFeaturePool->updateFeature( feature );
    error->setFixed( method );
    changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
  }
  else if ( method == RemoveObject )
  {
    mFeaturePool->deleteFeature( feature );
    error->setFixed( method );
    changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) );
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}
Example #17
0
void QgsGeometryContainedCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
    QgsGeometryContainedCheckError* coverError = static_cast<QgsGeometryContainedCheckError*>( error );

    QgsFeature feature;
    QgsFeature otherFeature;
    if ( !mFeaturePool->get( error->featureId(), feature ) ||
            !mFeaturePool->get( coverError->otherId(), otherFeature ) )
    {
        error->setObsolete();
        return;
    }

    // Check if error still applies
    QgsGeometry featureGeom = feature.geometry();
    QgsGeometryEngine* geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeom.geometry(), QgsGeometryCheckPrecision::tolerance() );

    if ( !geomEngine->within( *otherFeature.geometry().geometry() ) )
    {
        delete geomEngine;
        error->setObsolete();
        return;
    }
    delete geomEngine;

    // Fix error
    if ( method == NoChange )
    {
        error->setFixed( method );
    }
    else if ( method == Delete )
    {
        changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) );
        mFeaturePool->deleteFeature( feature );
        error->setFixed( method );
    }
    else
    {
        error->setFixFailed( tr( "Unknown method" ) );
    }
}
void QgsGeometrySegmentLengthCheck::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();
  QgsVertexId vidx = error->vidx();

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

  // Check if error still applies
  int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, vidx.part, vidx.ring );
  QgsPointV2 pi = geom->vertexAt( error->vidx() );
  QgsPointV2 pj = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex - 1 + nVerts ) % nVerts ) );
  double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) );
  if ( dist >= mMinLength )
  {
    error->setObsolete();
    return;
  }

  // Fix error
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}
Example #19
0
void QgsGeometryTypeCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
  const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
  Q_FOREACH ( QgsFeatureId featureid, featureIds )
  {
    if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
    QgsFeature feature;
    if ( !mFeaturePool->get( featureid, feature ) )
    {
      continue;
    }
    QgsGeometry featureGeom = feature.geometry();
    QgsAbstractGeometry* geom = featureGeom.geometry();

    QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom->wkbType() );
    if (( mAllowedTypes & ( 1 << type ) ) == 0 )
    {
      errors.append( new QgsGeometryTypeCheckError( this, featureid, geom->centroid(), type ) );
    }
  }
}
void QgsGeometrySegmentLengthCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
  const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
  Q_FOREACH ( QgsFeatureId featureid, featureIds )
  {
    if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
    QgsFeature feature;
    if ( !mFeaturePool->get( featureid, feature ) )
    {
      continue;
    }
    QgsGeometry featureGeom = feature.geometry();
    QgsAbstractGeometry* geom = featureGeom.geometry();

    for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
    {
      for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
      {
        int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing );
        if ( nVerts < 2 )
        {
          continue;
        }
        for ( int iVert = 0, jVert = nVerts - 1; iVert < nVerts; jVert = iVert++ )
        {
          QgsPointV2 pi = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) );
          QgsPointV2 pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) );
          double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) );
          if ( dist < mMinLength )
          {
            errors.append( new QgsGeometryCheckError( this, featureid, QgsPointV2( 0.5 * ( pi.x() + pj.x() ), 0.5 * ( pi.y() + pj.y() ) ), QgsVertexId( iPart, iRing, iVert ), dist, QgsGeometryCheckError::ValueLength ) );
          }
        }
      }
    }
  }
}
Example #21
0
void QgsGeometryHoleCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
  const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
  Q_FOREACH ( QgsFeatureId featureid, featureIds )
  {
    if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
    QgsFeature feature;
    if ( !mFeaturePool->get( featureid, feature ) )
    {
      continue;
    }

    QgsGeometry featureGeom = feature.geometry();
    QgsAbstractGeometry* geom = featureGeom.geometry();
    for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
    {
      // Rings after the first one are interiors
      for ( int iRing = 1, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
      {
        errors.append( new QgsGeometryCheckError( this, featureid, QgsGeometryCheckerUtils::getGeomPart( geom, iPart )->centroid(), QgsVertexId( iPart, iRing ) ) );
      }
    }
  }
}
Example #22
0
void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
  QString errMsg;
  QgsGeometryOverlapCheckError *overlapError = static_cast<QgsGeometryOverlapCheckError *>( error );

  QgsFeature feature;
  QgsFeature otherFeature;
  if ( !mFeaturePool->get( error->featureId(), feature ) ||
       !mFeaturePool->get( overlapError->otherId(), otherFeature ) )
  {
    error->setObsolete();
    return;
  }
  QgsGeometry featureGeom = feature.geometry();
  QgsAbstractGeometry *geom = featureGeom.geometry();
  QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::tolerance() );

  // Check if error still applies
  if ( !geomEngine->overlaps( *otherFeature.geometry().geometry() ) )
  {
    delete geomEngine;
    error->setObsolete();
    return;
  }
  QgsAbstractGeometry *interGeom = geomEngine->intersection( *otherFeature.geometry().geometry(), &errMsg );
  delete geomEngine;
  if ( !interGeom )
  {
    error->setFixFailed( tr( "Failed to compute intersection between overlapping features: %1" ).arg( errMsg ) );
    return;
  }

  // Search which overlap part this error parametrizes (using fuzzy-matching of the area and centroid...)
  QgsAbstractGeometry *interPart = nullptr;
  for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart )
  {
    QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart );
    if ( qAbs( part->area() - overlapError->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance() &&
         QgsGeometryCheckerUtils::pointsFuzzyEqual( part->centroid(), overlapError->location(), QgsGeometryCheckPrecision::reducedTolerance() ) )
    {
      interPart = part;
      break;
    }
  }
  if ( !interPart || interPart->isEmpty() )
  {
    delete interGeom;
    error->setObsolete();
    return;
  }

  // Fix error
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else if ( method == Subtract )
  {
    geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::reducedTolerance() );
    QgsAbstractGeometry *diff1 = geomEngine->difference( *interPart, &errMsg );
    delete geomEngine;
    if ( !diff1 || diff1->isEmpty() )
    {
      delete diff1;
      diff1 = nullptr;
    }
    else
    {
      QgsGeometryCheckerUtils::filter1DTypes( diff1 );
    }
    QgsGeometry otherFeatureGeom = otherFeature.geometry();
    QgsGeometryEngine *otherGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( otherFeatureGeom.geometry(), QgsGeometryCheckPrecision::reducedTolerance() );
    QgsAbstractGeometry *diff2 = otherGeomEngine->difference( *interPart, &errMsg );
    delete otherGeomEngine;
    if ( !diff2 || diff2->isEmpty() )
    {
      delete diff2;
      diff2 = nullptr;
    }
    else
    {
      QgsGeometryCheckerUtils::filter1DTypes( diff2 );
    }
    double shared1 = diff1 ? QgsGeometryCheckerUtils::sharedEdgeLength( diff1, interPart, QgsGeometryCheckPrecision::reducedPrecision() ) : 0;
    double shared2 = diff2 ? QgsGeometryCheckerUtils::sharedEdgeLength( diff2, interPart, QgsGeometryCheckPrecision::reducedPrecision() ) : 0;
    if ( shared1 == 0. || shared2 == 0. )
    {
      error->setFixFailed( tr( "Could not find shared edges between intersection and overlapping features" ) );
    }
    else
    {
      if ( shared1 < shared2 )
      {
        feature.setGeometry( QgsGeometry( diff1 ) );

        changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
        mFeaturePool->updateFeature( feature );

        delete diff2;
      }
      else
      {
        otherFeature.setGeometry( QgsGeometry( diff2 ) );

        changes[otherFeature.id()].append( Change( ChangeFeature, ChangeChanged ) );
        mFeaturePool->updateFeature( otherFeature );

        delete diff1;
      }

      error->setFixed( method );
    }
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
  delete interGeom;
}
Example #23
0
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 #24
0
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
  QgsFeature feature;
  if ( !mFeaturePool->get( error->featureId(), feature ) )
  {
    error->setObsolete();
    return;
  }
  QgsGeometry g = feature.geometry();
  QgsAbstractGeometry *geometry = g.geometry();
  QgsVertexId vidx = error->vidx();

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

  // Check if error still applies
  int n = QgsGeometryCheckerUtils::polyLineSize( geometry, vidx.part, vidx.ring );
  if ( n == 0 )
  {
    error->setObsolete();
    return;
  }
  const QgsPoint &p1 = geometry->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex - 1 + n ) % n ) );
  const QgsPoint &p2 = geometry->vertexAt( vidx );
  const QgsPoint &p3 = geometry->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) );
  QgsVector v21, v23;
  try
  {
    v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normalized();
    v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normalized();
  }
  catch ( const QgsException & )
  {
    error->setObsolete();
    return;
  }
  double angle = std::acos( v21 * v23 ) / M_PI * 180.0;
  if ( angle >= mMinAngle )
  {
    error->setObsolete();
    return;
  }

  // Fix error
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else if ( method == DeleteNode )
  {
    if ( !QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) )
    {
      error->setFixFailed( tr( "Resulting geometry is degenerate" ) );
    }
    else if ( !geometry->deleteVertex( error->vidx() ) )
    {
      error->setFixFailed( tr( "Failed to delete vertex" ) );
    }
    else
    {
      changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) );
      if ( QgsGeometryUtils::sqrDistance2D( p1, p3 ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance()
           && QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) &&
           geometry->deleteVertex( error->vidx() ) ) // error->vidx points to p3 after removing p2
      {
        changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ) );
      }
      feature.setGeometry( g );
      mFeaturePool->updateFeature( feature );
      error->setFixed( method );
    }
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}
Example #25
0
void QgsMapToolAddPart::cadCanvasReleaseEvent( QgsMapMouseEvent * e )
{
  //check if we operate on a vector layer
  QgsVectorLayer *vlayer = currentVectorLayer();
  if ( !vlayer )
  {
    notifyNotVectorLayer();
    return;
  }

  if ( !vlayer->isEditable() )
  {
    notifyNotEditableLayer();
    return;
  }

  bool isGeometryEmpty = false;
  if ( vlayer->selectedFeatures()[0].geometry().isEmpty() )
    isGeometryEmpty = true;

  if ( !checkSelection() )
  {
    stopCapturing();
    return;
  }

  int errorCode = 0;
  switch ( mode() )
  {
    case CapturePoint:
    {
      QgsPointV2 layerPoint;
      QgsPoint mapPoint = e->mapPoint();

      if ( nextPoint( QgsPointV2( mapPoint ), layerPoint ) != 0 )
      {
        QgsDebugMsg( "nextPoint failed" );
        return;
      }

      vlayer->beginEditCommand( tr( "Part added" ) );
      errorCode = vlayer->addPart( QgsPointSequence() << layerPoint );
    }
    break;

    case CaptureLine:
    case CapturePolygon:
    {
      //add point to list and to rubber band
      if ( e->button() == Qt::LeftButton )
      {
        int error = addVertex( e->mapPoint(), e->mapPointMatch() );
        if ( error == 1 )
        {
          QgsDebugMsg( "current layer is not a vector layer" );
          return;
        }
        else if ( error == 2 )
        {
          //problem with coordinate transformation
          emit messageEmitted( tr( "Coordinate transform error. Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING );
          return;
        }

        startCapturing();
        return;
      }
      else if ( e->button() != Qt::RightButton )
      {
        deleteTempRubberBand();

        return;
      }

      if ( !isCapturing() )
        return;

      if ( mode() == CapturePolygon )
      {
        closePolygon();
      }

      //does compoundcurve contain circular strings?
      //does provider support circular strings?
      bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
      bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries;

      QgsCurve* curveToAdd = nullptr;
      if ( hasCurvedSegments && providerSupportsCurvedSegments )
      {
        curveToAdd = captureCurve()->clone();
      }
      else
      {
        curveToAdd = captureCurve()->curveToLine();
      }

      vlayer->beginEditCommand( tr( "Part added" ) );
      if ( mode() == CapturePolygon )
      {
        //avoid intersections
        QgsCurvePolygon* cp = new QgsCurvePolygon();
        cp->setExteriorRing( curveToAdd );
        QgsGeometry* geom = new QgsGeometry( cp );
        geom->avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );

        const QgsCurvePolygon* cpGeom = dynamic_cast<const QgsCurvePolygon*>( geom->geometry() );
        if ( !cpGeom )
        {
          stopCapturing();
          delete geom;
          vlayer->destroyEditCommand();
          return;
        }

        errorCode = vlayer->addPart( cpGeom->exteriorRing()->clone() );
        delete geom;
      }
      else
      {
        errorCode = vlayer->addPart( curveToAdd );
      }
      stopCapturing();
    }
    break;
    default:
      Q_ASSERT( !"invalid capture mode" );
      errorCode = 6;
      break;
  }

  QString errorMessage;
  switch ( errorCode )
  {
    case 0:
    {
      // remove previous message
      emit messageDiscarded();

      //add points to other features to keep topology up-to-date
      bool topologicalEditing = QgsProject::instance()->topologicalEditing();
      if ( topologicalEditing )
      {
        addTopologicalPoints( points() );
      }

      vlayer->endEditCommand();

      vlayer->triggerRepaint();

      if (( !isGeometryEmpty ) && QgsWkbTypes::isSingleType( vlayer->wkbType() ) )
      {
        emit messageEmitted( tr( "Add part: Feature geom is single part and you've added more than one" ), QgsMessageBar::WARNING );
      }

      return;
    }

    case 1:
      errorMessage = tr( "Selected feature is not multi part." );
      break;

    case 2:
      errorMessage = tr( "New part's geometry is not valid." );
      break;

    case 3:
      errorMessage = tr( "New polygon ring not disjoint with existing polygons." );
      break;

    case 4:
      errorMessage = tr( "No feature selected. Please select a feature with the selection tool or in the attribute table" );
      break;

    case 5:
      errorMessage = tr( "Several features are selected. Please select only one feature to which an island should be added." );
      break;

    case 6:
      errorMessage = tr( "Selected geometry could not be found" );
      break;
  }

  emit messageEmitted( errorMessage, QgsMessageBar::WARNING );
  vlayer->destroyEditCommand();
}