示例#1
0
ErrorList topolTest::checkPolygonContainsPoint( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent )
{
  Q_UNUSED( tolerance );
  Q_UNUSED( isExtent );

  int i = 0;
  ErrorList errorList;

  if ( layer1->geometryType() != QgsWkbTypes::PolygonGeometry )
  {
    return errorList;
  }

  if ( layer2->geometryType() != QgsWkbTypes::PointGeometry )
  {
    return errorList;
  }

  QgsSpatialIndex *index = mLayerIndexes[layer2->id()];

  QList<FeatureLayer>::Iterator it;
  for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it )
  {
    if ( !( ++i % 100 ) )
      emit progress( i );
    if ( testCanceled() )
      break;
    QgsGeometry g1 = it->feature.geometry();
    QgsRectangle bb = g1.boundingBox();
    QList<QgsFeatureId> crossingIds;
    crossingIds = index->intersects( bb );
    QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin();
    QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.constEnd();
    bool touched = false;
    for ( ; cit != crossingIdsEnd; ++cit )
    {
      QgsFeature &f = mFeatureMap2[*cit].feature;
      QgsGeometry g2 = f.geometry();
      if ( g2.isNull() || !_canExportToGeos( g2 ) )
      {
        QgsMessageLog::logMessage( tr( "Second geometry missing or GEOS import failed." ), tr( "Topology plugin" ) );
        continue;
      }
      if ( g1.contains( g2 ) )
      {
        touched = true;
        break;
      }
    }
    if ( !touched )
    {
      QList<FeatureLayer> fls;
      fls << *it << *it;
      //bb.scale(10);
      TopolErrorPolygonContainsPoint *err = new TopolErrorPolygonContainsPoint( bb, g1, fls );
      errorList << err;
    }
  }
  return errorList;
}
static void assignAnchors( QgsSpatialIndex &index, QVector<AnchorPoint> &pnts, double thresh )
{
  double thresh2 = thresh * thresh;
  int nanchors = 0, ntosnap = 0;
  for ( int point = 0; point < pnts.count(); ++point )
  {
    if ( pnts[point].anchor >= 0 )
      continue;

    pnts[point].anchor = -2; // make it anchor
    nanchors++;

    // Find points in threshold
    double x = pnts[point].x, y = pnts[point].y;
    QgsRectangle rect( x - thresh, y - thresh, x + thresh, y + thresh );

    const QList<QgsFeatureId> ids = index.intersects( rect );
    for ( QgsFeatureId pointb : ids )
    {
      if ( pointb == point )
        continue;

      double dx = pnts[pointb].x - pnts[point].x;
      double dy = pnts[pointb].y - pnts[point].y;
      double dist2 = dx * dx + dy * dy;
      if ( dist2 > thresh2 )
        continue;   // outside threshold

      if ( pnts[pointb].anchor == -1 )
      {
        // doesn't have an anchor yet
        pnts[pointb].anchor = point;
        ntosnap++;
      }
      else if ( pnts[pointb].anchor >= 0 )
      {
        // check distance to previously assigned anchor
        double dx2 = pnts[pnts[pointb].anchor].x - pnts[pointb].x;
        double dy2 = pnts[pnts[pointb].anchor].y - pnts[pointb].y;
        double dist2_a = dx2 * dx2 + dy2 * dy2;
        if ( dist2 < dist2_a )
          pnts[pointb].anchor = point;   // replace old anchor
      }
    }
  }
}
static bool snapPoint( QgsPoint *pt, QgsSpatialIndex &index, QVector<AnchorPoint> &pnts )
{
  // Find point ( should always find one point )
  QList<QgsFeatureId> fids = index.intersects( QgsRectangle( pt->x(), pt->y(), pt->x(), pt->y() ) );
  Q_ASSERT( fids.count() == 1 );

  int spoint = fids[0];
  int anchor = pnts[spoint].anchor;

  if ( anchor >= 0 )
  {
    // to be snapped
    pt->setX( pnts[anchor].x );
    pt->setY( pnts[anchor].y );
    return true;
  }

  return false;
}
示例#4
0
bool QgsTransectSample::otherTransectWithinDistance( QgsGeometry* geom, double minDistLayerUnit, double minDistance, QgsSpatialIndex& sIndex,
    const QMap< QgsFeatureId, QgsGeometry* >& lineFeatureMap, QgsDistanceArea& da )
{
  if ( !geom )
  {
    return false;
  }

  QgsGeometry* buffer = geom->buffer( minDistLayerUnit, 8 );
  if ( !buffer )
  {
    return false;
  }
  QgsRectangle rect = buffer->boundingBox();
  QList<QgsFeatureId> lineIdList = sIndex.intersects( rect );

  QList<QgsFeatureId>::const_iterator lineIdIt = lineIdList.constBegin();
  for ( ; lineIdIt != lineIdList.constEnd(); ++lineIdIt )
  {
    const QMap< QgsFeatureId, QgsGeometry* >::const_iterator idMapIt = lineFeatureMap.find( *lineIdIt );
    if ( idMapIt != lineFeatureMap.constEnd() )
    {
      double dist = 0;
      QgsPoint pt1, pt2;
      closestSegmentPoints( *geom, *( idMapIt.value() ), dist, pt1, pt2 );
      dist = da.measureLine( pt1, pt2 ); //convert degrees to meters if necessary

      if ( dist < minDistance )
      {
        delete buffer;
        return true;
      }
    }
  }

  delete buffer;
  return false;
}
static void buildSnapIndex( QgsFeatureIterator &fi, QgsSpatialIndex &index, QVector<AnchorPoint> &pnts, QgsFeedback *feedback, int &count, int totalCount )
{
  QgsFeature f;
  int pntId = 0;

  while ( fi.nextFeature( f ) )
  {
    if ( feedback->isCanceled() )
      break;

    QgsGeometry g = f.geometry();

    for ( auto it = g.vertices_begin(); it != g.vertices_end(); ++it )
    {
      QgsPoint pt = *it;
      QgsRectangle rect( pt.x(), pt.y(), pt.x(), pt.y() );

      QList<QgsFeatureId> ids = index.intersects( rect );
      if ( ids.isEmpty() )
      {
        // add to tree and to structure
        index.insertFeature( pntId, pt.boundingBox() );

        AnchorPoint xp;
        xp.x = pt.x();
        xp.y = pt.y();
        xp.anchor = -1;
        pnts.append( xp );
        pntId++;
      }
    }

    ++count;
    feedback->setProgress( 100. * count / totalCount );
  }
}
示例#6
0
void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback )
{
  int count = 0;
  int totalCount = source.featureCount();
  if ( totalCount == 0 )
    return;  // nothing to do here

  QgsFeatureId newFid = -1;

  QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::geometryType( QgsWkbTypes::multiType( source.wkbType() ) );

  QgsFeatureRequest requestOnlyGeoms;
  requestOnlyGeoms.setSubsetOfAttributes( QgsAttributeList() );

  QgsFeatureRequest requestOnlyAttrs;
  requestOnlyAttrs.setFlags( QgsFeatureRequest::NoGeometry );

  QgsFeatureRequest requestOnlyIds;
  requestOnlyIds.setFlags( QgsFeatureRequest::NoGeometry );
  requestOnlyIds.setSubsetOfAttributes( QgsAttributeList() );

  // make a set of used feature IDs so that we do not try to reuse them for newly added features
  QgsFeature f;
  QSet<QgsFeatureId> fids;
  QgsFeatureIterator it = source.getFeatures( requestOnlyIds );
  while ( it.nextFeature( f ) )
  {
    if ( feedback->isCanceled() )
      return;

    fids.insert( f.id() );
  }

  QHash<QgsFeatureId, QgsGeometry> geometries;
  QgsSpatialIndex index;
  QHash<QgsFeatureId, QList<QgsFeatureId> > intersectingIds;  // which features overlap a particular area

  // resolve intersections

  it = source.getFeatures( requestOnlyGeoms );
  while ( it.nextFeature( f ) )
  {
    if ( feedback->isCanceled() )
      return;

    QgsFeatureId fid1 = f.id();
    QgsGeometry g1 = f.geometry();
    std::unique_ptr< QgsGeometryEngine > g1engine;

    geometries.insert( fid1, g1 );
    index.insertFeature( f );

    QgsRectangle bbox( f.geometry().boundingBox() );
    const QList<QgsFeatureId> ids = index.intersects( bbox );
    for ( QgsFeatureId fid2 : ids )
    {
      if ( fid1 == fid2 )
        continue;

      if ( !g1engine )
      {
        // use prepared geometries for faster intersection tests
        g1engine.reset( QgsGeometry::createGeometryEngine( g1.constGet() ) );
        g1engine->prepareGeometry();
      }

      QgsGeometry g2 = geometries.value( fid2 );
      if ( !g1engine->intersects( g2.constGet() ) )
        continue;

      QgsGeometry geomIntersection = g1.intersection( g2 );
      if ( !sanitizeIntersectionResult( geomIntersection, geometryType ) )
        continue;

      //
      // add intersection geometry
      //

      // figure out new fid
      while ( fids.contains( newFid ) )
        --newFid;
      fids.insert( newFid );

      geometries.insert( newFid, geomIntersection );
      QgsFeature fx( newFid );
      fx.setGeometry( geomIntersection );

      index.insertFeature( fx );

      // figure out which feature IDs belong to this intersection. Some of the IDs can be of the newly
      // created geometries - in such case we need to retrieve original IDs
      QList<QgsFeatureId> lst;
      if ( intersectingIds.contains( fid1 ) )
        lst << intersectingIds.value( fid1 );
      else
        lst << fid1;
      if ( intersectingIds.contains( fid2 ) )
        lst << intersectingIds.value( fid2 );
      else
        lst << fid2;
      intersectingIds.insert( newFid, lst );

      //
      // update f1
      //

      QgsGeometry g12 = g1.difference( g2 );

      index.deleteFeature( f );
      geometries.remove( fid1 );

      if ( sanitizeDifferenceResult( g12 ) )
      {
        geometries.insert( fid1, g12 );

        QgsFeature f1x( fid1 );
        f1x.setGeometry( g12 );
        index.insertFeature( f1x );
      }

      //
      // update f2
      //

      QgsGeometry g21 = g2.difference( g1 );

      QgsFeature f2old( fid2 );
      f2old.setGeometry( g2 );
      index.deleteFeature( f2old );

      geometries.remove( fid2 );

      if ( sanitizeDifferenceResult( g21 ) )
      {
        geometries.insert( fid2, g21 );

        QgsFeature f2x( fid2 );
        f2x.setGeometry( g21 );
        index.insertFeature( f2x );
      }

      // update our temporary copy of the geometry to what is left from it
      g1 = g12;
      g1engine.reset();
    }

    ++count;
    feedback->setProgress( count / ( double ) totalCount * 100. );
  }

  // release some memory of structures we don't need anymore

  fids.clear();
  index = QgsSpatialIndex();

  // load attributes

  QHash<QgsFeatureId, QgsAttributes> attributesHash;
  it = source.getFeatures( requestOnlyAttrs );
  while ( it.nextFeature( f ) )
  {
    if ( feedback->isCanceled() )
      return;

    attributesHash.insert( f.id(), f.attributes() );
  }

  // store stuff in the sink

  for ( auto i = geometries.constBegin(); i != geometries.constEnd(); ++i )
  {
    if ( feedback->isCanceled() )
      return;

    QgsFeature outFeature( i.key() );
    outFeature.setGeometry( i.value() );

    if ( intersectingIds.contains( i.key() ) )
    {
      const QList<QgsFeatureId> ids = intersectingIds.value( i.key() );
      for ( QgsFeatureId id : ids )
      {
        outFeature.setAttributes( attributesHash.value( id ) );
        sink.addFeature( outFeature, QgsFeatureSink::FastInsert );
      }
    }
    else
    {
      outFeature.setAttributes( attributesHash.value( i.key() ) );
      sink.addFeature( outFeature, QgsFeatureSink::FastInsert );
    }
  }
}
示例#7
0
ErrorList topolTest::checkPointCoveredByLineEnds( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent )
{
  int i = 0;
  ErrorList errorList;


  if ( layer1->geometryType() != QgsWkbTypes::PointGeometry )
  {
    return errorList;
  }

  if ( layer2->geometryType() != QgsWkbTypes::LineGeometry )
  {
    return errorList;
  }

  QgsSpatialIndex *index = mLayerIndexes[layer2->id()];
  QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() );

  QList<FeatureLayer>::Iterator it;
  for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it )
  {
    if ( !( ++i % 100 ) )
      emit progress( i );
    if ( testCanceled() )
      break;
    QgsGeometry g1 = it->feature.geometry();
    QgsRectangle bb = g1.boundingBox();
    QList<QgsFeatureId> crossingIds;
    crossingIds = index->intersects( bb );
    QList<QgsFeatureId>::ConstIterator cit = crossingIds.constBegin();
    QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.constEnd();
    bool touched = false;
    for ( ; cit != crossingIdsEnd; ++cit )
    {
      QgsFeature &f = mFeatureMap2[*cit].feature;
      QgsGeometry g2 = f.geometry();
      if ( g2.isNull() || !_canExportToGeos( g2 ) )
      {
        QgsMessageLog::logMessage( tr( "Second geometry missing or GEOS import failed." ), tr( "Topology plugin" ) );
        continue;
      }
      QgsPolylineXY g2Line = g2.asPolyline();
      QgsGeometry startPoint = QgsGeometry::fromPointXY( g2Line.at( 0 ) );
      QgsGeometry endPoint = QgsGeometry::fromPointXY( g2Line.last() );
      touched = g1.intersects( startPoint ) || g1.intersects( endPoint );

      if ( touched )
      {
        break;
      }
    }
    if ( !touched )
    {
      QgsGeometry conflictGeom = g1;
      if ( isExtent )
      {
        if ( canvasExtentPoly.disjoint( conflictGeom ) )
        {
          continue;
        }
      }

      QList<FeatureLayer> fls;
      fls << *it << *it;
      //bb.scale(10);

      TopolErrorPointNotCoveredByLineEnds *err = new TopolErrorPointNotCoveredByLineEnds( bb, conflictGeom, fls );
      errorList << err;
    }
  }
  return errorList;
}
示例#8
0
ErrorList topolTest::checkOverlapWithLayer( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent )
{
  int i = 0;
  ErrorList errorList;

  bool skipItself = layer1 == layer2;
  QgsSpatialIndex *index = mLayerIndexes[layer2->id()];

  QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() );

  QList<FeatureLayer>::iterator it;
  for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it )
  {
    if ( !( ++i % 100 ) )
      emit progress( i );

    if ( testCanceled() )
      break;

    QgsGeometry g1 = it->feature.geometry();
    QgsRectangle bb = g1.boundingBox();

    QList<QgsFeatureId> crossingIds;
    crossingIds = index->intersects( bb );

    QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin();
    QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end();
    for ( ; cit != crossingIdsEnd; ++cit )
    {
      QgsFeature &f = mFeatureMap2[*cit].feature;
      QgsGeometry g2 = f.geometry();

      // skip itself, when invoked with the same layer
      if ( skipItself && f.id() == it->feature.id() )
        continue;

      if ( g2.isNull() )
      {
        QgsMessageLog::logMessage( tr( "Second geometry missing." ), tr( "Topology plugin" ) );
        continue;
      }

      if ( g1.overlaps( g2 ) )
      {
        QgsRectangle r = bb;
        QgsRectangle r2 = g2.boundingBox();
        r.combineExtentWith( r2 );

        QgsGeometry conflictGeom = g1.intersection( g2 );
        // could this for some reason return NULL?
        if ( conflictGeom.isNull() )
        {
          continue;
        }

        if ( isExtent )
        {
          if ( canvasExtentPoly.disjoint( conflictGeom ) )
          {
            continue;
          }
          if ( canvasExtentPoly.crosses( conflictGeom ) )
          {
            conflictGeom = conflictGeom.intersection( canvasExtentPoly );
          }
        }

        //c = new QgsGeometry;

        QList<FeatureLayer> fls;
        FeatureLayer fl;
        fl.feature = f;
        fl.layer = layer2;
        fls << *it << fl;
        TopolErrorIntersection *err = new TopolErrorIntersection( r, conflictGeom, fls );

        errorList << err;
      }
    }
  }
  return errorList;
}
示例#9
0
ErrorList topolTest::checkPointCoveredBySegment( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent )
{
  int i = 0;

  ErrorList errorList;

  if ( layer1->geometryType() != QgsWkbTypes::PointGeometry )
  {
    return errorList;
  }
  if ( layer2->geometryType() == QgsWkbTypes::PointGeometry )
  {
    return errorList;
  }

  QgsSpatialIndex *index = mLayerIndexes[layer2->id()];
  QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() );

  QList<FeatureLayer>::Iterator it;
  for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it )
  {
    if ( !( ++i % 100 ) )
      emit progress( i );

    if ( testCanceled() )
      break;

    QgsGeometry g1 = it->feature.geometry();
    QgsRectangle bb = g1.boundingBox();

    QList<QgsFeatureId> crossingIds;
    crossingIds = index->intersects( bb );

    QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin();
    QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end();

    bool touched = false;

    for ( ; cit != crossingIdsEnd; ++cit )
    {
      QgsFeature &f = mFeatureMap2[*cit].feature;
      QgsGeometry g2 = f.geometry();

      if ( g2.isNull() )
      {
        QgsMessageLog::logMessage( tr( "Invalid geometry in covering test." ), tr( "Topology plugin" ) );
        continue;
      }

      // test if point touches other geometry
      if ( g1.touches( g2 ) )
      {
        touched = true;
        break;
      }
    }

    if ( !touched )
    {
      QgsGeometry conflictGeom = QgsGeometry( g1 );

      if ( isExtent )
      {
        if ( canvasExtentPoly.disjoint( conflictGeom ) )
        {
          continue;
        }
      }

      QList<FeatureLayer> fls;
      fls << *it << *it;
      //bb.scale(10);

      TopolErrorCovered *err = new TopolErrorCovered( bb, conflictGeom, fls );

      errorList << err;
    }
  }
  return errorList;
}
示例#10
0
ErrorList topolTest::checkOverlaps( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent )
{
  Q_UNUSED( layer2 );
  int i = 0;
  ErrorList errorList;

  // could be enabled for lines and points too
  // so duplicate rule may be removed?

  if ( layer1->geometryType() != QgsWkbTypes::PolygonGeometry )
  {
    return errorList;
  }

  QList<QgsFeatureId> *duplicateIds = new QList<QgsFeatureId>();

  QgsSpatialIndex *index = mLayerIndexes[layer1->id()];
  if ( !index )
  {
    qDebug() << "no index present";
    delete duplicateIds;
    return errorList;
  }

  QMap<QgsFeatureId, FeatureLayer>::const_iterator it;
  for ( it = mFeatureMap2.constBegin(); it != mFeatureMap2.constEnd(); ++it )
  {
    if ( !( ++i % 100 ) )
      emit progress( i );

    QgsFeatureId currentId = it->feature.id();

    if ( duplicateIds->contains( currentId ) )
    {
      //is already a duplicate geometry..skip..
      continue;
    }

    if ( testCanceled() )
      break;

    QgsGeometry g1 = it->feature.geometry();

    if ( !g1.isGeosValid() )
    {
      qDebug() << "invalid geometry(g1) found..skipping.." << it->feature.id();
      continue;
    }

    QgsRectangle bb = g1.boundingBox();

    QList<QgsFeatureId> crossingIds;
    crossingIds = index->intersects( bb );

    QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin();
    QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end();

    bool duplicate = false;

    QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() );

    for ( ; cit != crossingIdsEnd; ++cit )
    {
      duplicate = false;
      // skip itself
      if ( mFeatureMap2[*cit].feature.id() == it->feature.id() )
        continue;

      QgsGeometry g2 = mFeatureMap2[*cit].feature.geometry();
      if ( g2.isNull() )
      {
        QgsMessageLog::logMessage( tr( "Invalid second geometry in overlaps test." ), tr( "Topology plugin" ) );
        continue;
      }

      if ( !_canExportToGeos( g2 ) )
      {
        QgsMessageLog::logMessage( tr( "Failed to import second geometry into GEOS in overlaps test." ), tr( "Topology plugin" ) );
        continue;
      }

      if ( !g2.isGeosValid() )
      {
        QgsMessageLog::logMessage( tr( "Skipping invalid second geometry of feature %1 in overlaps test." ).arg( it->feature.id() ), tr( "Topology plugin" ) );
        continue;
      }


      qDebug() << "checking overlap for" << it->feature.id();
      if ( g1.overlaps( g2 ) )
      {
        duplicate = true;
        duplicateIds->append( mFeatureMap2[*cit].feature.id() );
      }

      if ( duplicate )
      {
        QList<FeatureLayer> fls;
        fls << *it << *it;
        QgsGeometry conflictGeom = g1.intersection( g2 );

        if ( isExtent )
        {
          if ( canvasExtentPoly.disjoint( conflictGeom ) )
          {
            continue;
          }
          if ( canvasExtentPoly.crosses( conflictGeom ) )
          {
            conflictGeom = conflictGeom.intersection( canvasExtentPoly );
          }
        }

        TopolErrorOverlaps *err = new TopolErrorOverlaps( bb, conflictGeom, fls );

        errorList << err;
      }

    }
  }

  delete duplicateIds;

  return errorList;
}
示例#11
0
ErrorList topolTest::checkDuplicates( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent )
{
  Q_UNUSED( layer2 );
  //TODO: multilines - check all separate pieces
  int i = 0;
  ErrorList errorList;

  QList<QgsFeatureId> duplicateIds;

  QgsSpatialIndex *index = mLayerIndexes[layer1->id()];

  QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() );

  QMap<QgsFeatureId, FeatureLayer>::const_iterator it;
  for ( it = mFeatureMap2.constBegin(); it != mFeatureMap2.constEnd(); ++it )
  {
    if ( !( ++i % 100 ) )
      emit progress( i );

    QgsFeatureId currentId = it->feature.id();

    if ( duplicateIds.contains( currentId ) )
    {
      //is already a duplicate geometry..skip..
      continue;
    }

    if ( testCanceled() )
      break;

    QgsGeometry g1 = it->feature.geometry();
    QgsRectangle bb = g1.boundingBox();

    QList<QgsFeatureId> crossingIds;
    crossingIds = index->intersects( bb );

    QList<QgsFeatureId>::ConstIterator cit = crossingIds.constBegin();
    QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.constEnd();

    bool duplicate = false;

    for ( ; cit != crossingIdsEnd; ++cit )
    {
      duplicate = false;
      // skip itself
      if ( mFeatureMap2[*cit].feature.id() == it->feature.id() )
        continue;

      QgsGeometry g2 = mFeatureMap2[*cit].feature.geometry();
      if ( g2.isNull() )
      {
        QgsMessageLog::logMessage( tr( "Invalid second geometry in duplicate geometry test." ), tr( "Topology plugin" ) );
        continue;
      }

      if ( !_canExportToGeos( g2 ) )
      {
        QgsMessageLog::logMessage( tr( "Failed to import second geometry into GEOS in duplicate geometry test." ), tr( "Topology plugin" ) );
        continue;
      }

      if ( g1.isGeosEqual( g2 ) )
      {
        duplicate = true;
        duplicateIds.append( mFeatureMap2[*cit].feature.id() );
      }

      if ( duplicate )
      {


        QList<FeatureLayer> fls;
        fls << *it << *it;
        QgsGeometry conflict( g1 );

        if ( isExtent )
        {
          if ( canvasExtentPoly.disjoint( conflict ) )
          {
            continue;
          }
          if ( canvasExtentPoly.crosses( conflict ) )
          {
            conflict = conflict.intersection( canvasExtentPoly );
          }
        }

        TopolErrorDuplicates *err = new TopolErrorDuplicates( bb, conflict, fls );

        errorList << err;
      }

    }

  }
  return errorList;
}
示例#12
0
ErrorList topolTest::checkDuplicates( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent )
{
  Q_UNUSED( tolerance );
  Q_UNUSED( layer2 );
  //TODO: multilines - check all separate pieces
  int i = 0;
  ErrorList errorList;

  QList<QgsFeatureId> duplicateIds;

  QgsSpatialIndex* index = mLayerIndexes[layer1->id()];

  QgsGeometry* canvasExtentPoly = QgsGeometry::fromWkt( theQgsInterface->mapCanvas()->extent().asWktPolygon() );

  QMap<QgsFeatureId, FeatureLayer>::Iterator it;
  QMap<QgsFeatureId, FeatureLayer>::ConstIterator FeatureListEnd = mFeatureMap2.end();
  for ( it = mFeatureMap2.begin(); it != FeatureListEnd; ++it )
  {
    if ( !( ++i % 100 ) )
      emit progress( i );

    QgsFeatureId currentId = it->feature.id();

    if ( duplicateIds.contains( currentId ) )
    {
      //is already a duplicate geometry..skip..
      continue;
    }

    if ( testCancelled() )
      break;

    QgsGeometry* g1 = it->feature.geometry();
    QgsRectangle bb = g1->boundingBox();

    QList<QgsFeatureId> crossingIds;
    crossingIds = index->intersects( bb );

    QList<QgsFeatureId>::Iterator cit = crossingIds.begin();
    QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end();

    bool duplicate = false;

    for ( ; cit != crossingIdsEnd; ++cit )
    {
      duplicate = false;
      // skip itself
      if ( mFeatureMap2[*cit].feature.id() == it->feature.id() )
        continue;

      const QgsGeometry* g2 = mFeatureMap2[*cit].feature.constGeometry();
      if ( !g2 )
      {
        QgsMessageLog::logMessage( tr( "Invalid second geometry in duplicate geometry test." ), tr( "Topology plugin" ) );
        continue;
      }

      if ( !g2->asGeos() )
      {
        QgsMessageLog::logMessage( tr( "Failed to import second geometry into GEOS in duplicate geometry test." ), tr( "Topology plugin" ) );
        continue;
      }

      if ( g1->equals( g2 ) )
      {
        duplicate = true;
        duplicateIds.append( mFeatureMap2[*cit].feature.id() );
      }

      if ( duplicate )
      {


        QList<FeatureLayer> fls;
        fls << *it << *it;
        QScopedPointer<QgsGeometry> conflict( new QgsGeometry( *g1 ) );

        if ( isExtent )
        {
          if ( canvasExtentPoly->disjoint( conflict.data() ) )
          {
            continue;
          }
          if ( canvasExtentPoly->crosses( conflict.data() ) )
          {
            conflict.reset( conflict->intersection( canvasExtentPoly ) );
          }
        }

        TopolErrorDuplicates* err = new TopolErrorDuplicates( bb, conflict.take(), fls );

        errorList << err;
      }

    }

  }
  delete canvasExtentPoly;
  return errorList;
}
示例#13
0
ErrorList topolTest::checkCloseFeature( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent )
{
  Q_UNUSED( isExtent );
  ErrorList errorList;
  QgsSpatialIndex* index = 0;

  bool badG1 = false, badG2 = false;
  bool skipItself = layer1 == layer2;

  int i = 0;
  QList<FeatureLayer>::Iterator it;
  QList<FeatureLayer>::ConstIterator FeatureListEnd = mFeatureList1.end();
  for ( it = mFeatureList1.begin(); it != FeatureListEnd; ++it )
  {
    if ( !( ++i % 100 ) )
      emit progress( i );

    if ( testCancelled() )
      break;

    QgsGeometry* g1 = it->feature.geometry();
    if ( !g1 || !g1->asGeos() )
    {
      badG1 = true;
      continue;
    }

    QgsRectangle bb = g1->boundingBox();

    // increase bounding box by tolerance
    QgsRectangle frame( bb.xMinimum() - tolerance, bb.yMinimum() - tolerance, bb.xMaximum() + tolerance, bb.yMaximum() + tolerance );

    QList<QgsFeatureId> crossingIds;
    crossingIds = index->intersects( frame );

    QList<QgsFeatureId>::Iterator cit = crossingIds.begin();
    QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end();

    for ( ; cit != crossingIdsEnd; ++cit )
    {
      QgsFeature& f = mFeatureMap2[*cit].feature;
      QgsGeometry* g2 = f.geometry();

      // skip itself, when invoked with the same layer
      if ( skipItself && f.id() == it->feature.id() )
        continue;

      if ( !g2 || !g2->asGeos() )
      {
        badG2 = true;
        continue;
      }

      if ( g1->distance( *g2 ) < tolerance )
      {
        QgsRectangle r = g2->boundingBox();
        r.combineExtentWith( &bb );

        QList<FeatureLayer> fls;
        FeatureLayer fl;
        fl.feature = f;
        fl.layer = layer2;
        fls << *it << fl;
        QgsGeometry* conflict = new QgsGeometry( *g2 );
        TopolErrorClose* err = new TopolErrorClose( r, conflict, fls );
        //TopolErrorClose* err = new TopolErrorClose(r, g2, fls);

        errorList << err;
      }
    }
  }

  if ( badG2 )
    QgsMessageLog::logMessage( tr( "Invalid second geometry." ), tr( "Topology plugin" ) );

  if ( badG1 )
    QgsMessageLog::logMessage( tr( "Invalid first geometry." ), tr( "Topology plugin" ) );

  return errorList;
}
示例#14
0
ErrorList topolTest::checkPointInPolygon( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent )
{
  Q_UNUSED( tolerance );

  int i = 0;
  ErrorList errorList;

  if ( layer1->geometryType() != QGis::Point )
  {
    return errorList;
  }

  if ( layer2->geometryType() != QGis::Polygon )
  {
    return errorList;
  }

  QgsSpatialIndex* index = mLayerIndexes[layer2->id()];

  QgsGeometry* canvasExtentPoly = QgsGeometry::fromWkt( theQgsInterface->mapCanvas()->extent().asWktPolygon() );

  QList<FeatureLayer>::Iterator it;
  for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it )
  {
    if ( !( ++i % 100 ) )
      emit progress( i );
    if ( testCancelled() )
      break;
    QgsGeometry* g1 = it->feature.geometry();
    QgsRectangle bb = g1->boundingBox();
    QList<QgsFeatureId> crossingIds;
    crossingIds = index->intersects( bb );
    QList<QgsFeatureId>::Iterator cit = crossingIds.begin();
    QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end();
    bool touched = false;
    for ( ; cit != crossingIdsEnd; ++cit )
    {
      QgsFeature& f = mFeatureMap2[*cit].feature;
      const QgsGeometry* g2 = f.constGeometry();
      if ( !g2 || !g2->asGeos() )
      {
        QgsMessageLog::logMessage( tr( "Second geometry missing or GEOS import failed." ), tr( "Topology plugin" ) );
        continue;
      }
      if ( g2->contains( g1 ) )
      {
        touched = true;
        break;
      }
    }
    if ( !touched )
    {
      QgsGeometry* conflictGeom = new QgsGeometry( *g1 );

      if ( isExtent )
      {
        if ( canvasExtentPoly->disjoint( conflictGeom ) )
        {
          delete conflictGeom;
          continue;
        }
      }

      QList<FeatureLayer> fls;
      fls << *it << *it;
      //bb.scale(10);

      TopolErrorPointNotInPolygon* err = new TopolErrorPointNotInPolygon( bb, conflictGeom, fls );
      errorList << err;
    }
  }

  delete canvasExtentPoly;
  return errorList;
}
static bool snapLineString( QgsLineString *linestring, QgsSpatialIndex &index, QVector<AnchorPoint> &pnts, double thresh )
{
  QVector<QgsPoint> newPoints;
  QVector<int> anchors;  // indexes of anchors for vertices
  double thresh2 = thresh * thresh;
  double minDistX, minDistY;   // coordinates of the closest point on the segment line
  bool changed = false;

  // snap vertices
  for ( int v = 0; v < linestring->numPoints(); v++ )
  {
    double x = linestring->xAt( v );
    double y = linestring->yAt( v );
    QgsRectangle rect( x, y, x, y );

    // Find point ( should always find one point )
    QList<QgsFeatureId> fids = index.intersects( rect );
    Q_ASSERT( fids.count() == 1 );

    int spoint = fids.first();
    int anchor = pnts[spoint].anchor;
    if ( anchor >= 0 )
    {
      // to be snapped
      linestring->setXAt( v, pnts[anchor].x );
      linestring->setYAt( v, pnts[anchor].y );
      anchors.append( anchor ); // point on new location
      changed = true;
    }
    else
    {
      anchors.append( spoint ); // old point
    }
  }

  // Snap all segments to anchors in threshold
  for ( int v = 0; v < linestring->numPoints() - 1; v++ )
  {
    double x1 = linestring->xAt( v );
    double x2 = linestring->xAt( v + 1 );
    double y1 = linestring->yAt( v );
    double y2 = linestring->yAt( v + 1 );

    newPoints << linestring->pointN( v );

    // Box
    double xmin = x1, xmax = x2, ymin = y1, ymax = y2;
    if ( xmin > xmax )
      std::swap( xmin, xmax );
    if ( ymin > ymax )
      std::swap( ymin, ymax );

    QgsRectangle rect( xmin - thresh, ymin - thresh, xmax + thresh, ymax + thresh );

    // Find points
    const QList<QgsFeatureId> fids = index.intersects( rect );

    QVector<AnchorAlongSegment> newVerticesAlongSegment;

    // Snap to anchor in threshold different from end points
    for ( QgsFeatureId fid : fids )
    {
      int spoint = fid;

      if ( spoint == anchors[v] || spoint == anchors[v + 1] )
        continue; // end point
      if ( pnts[spoint].anchor >= 0 )
        continue; // point is not anchor

      // Check the distance
      double dist2 = QgsGeometryUtils::sqrDistToLine( pnts[spoint].x, pnts[spoint].y, x1, y1, x2, y2, minDistX, minDistY, 0 );
      // skip points that are behind segment's endpoints or extremely close to them
      double dx1 = minDistX - x1, dx2 = minDistX - x2;
      double dy1 = minDistY - y1, dy2 = minDistY - y2;
      bool isOnSegment = !qgsDoubleNear( dx1 * dx1 + dy1 * dy1, 0 ) && !qgsDoubleNear( dx2 * dx2 + dy2 * dy2, 0 );
      if ( isOnSegment && dist2 <= thresh2 )
      {
        // an anchor is in the threshold
        AnchorAlongSegment item;
        item.anchor = spoint;
        item.along = QgsPointXY( x1, y1 ).distance( minDistX, minDistY );
        newVerticesAlongSegment << item;
      }
    }

    if ( !newVerticesAlongSegment.isEmpty() )
    {
      // sort by distance along the segment
      std::sort( newVerticesAlongSegment.begin(), newVerticesAlongSegment.end(), []( const AnchorAlongSegment & p1, const AnchorAlongSegment & p2 )
      {
        return ( p1.along < p2.along ? -1 : ( p1.along > p2.along ) );
      } );

      // insert new vertices
      for ( int i = 0; i < newVerticesAlongSegment.count(); i++ )
      {
        int anchor = newVerticesAlongSegment[i].anchor;
        newPoints << QgsPoint( pnts[anchor].x, pnts[anchor].y, 0 );
      }
      changed = true;
    }
  }

  // append end point
  newPoints << linestring->pointN( linestring->numPoints() - 1 );

  // replace linestring's points
  if ( changed )
    linestring->setPoints( newPoints );

  return changed;
}
示例#16
0
QVariantMap QgsSplitWithLinesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
  std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
  if ( !source )
    throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );

  std::unique_ptr< QgsFeatureSource > linesSource( parameterAsSource( parameters, QStringLiteral( "LINES" ), context ) );
  if ( !linesSource )
    throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "LINES" ) ) );

  bool sameLayer = parameters.value( QStringLiteral( "INPUT" ) ) == parameters.value( QStringLiteral( "LINES" ) );

  QString dest;
  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(),
                                          QgsWkbTypes::multiType( source->wkbType() ),  source->sourceCrs() ) );
  if ( !sink )
    throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );

  QgsSpatialIndex spatialIndex;
  QMap< QgsFeatureId, QgsGeometry > splitGeoms;
  QgsFeatureRequest request;
  request.setSubsetOfAttributes( QgsAttributeList() );
  request.setDestinationCrs( source->sourceCrs(), context.transformContext() );

  QgsFeatureIterator splitLines = linesSource->getFeatures( request );
  QgsFeature aSplitFeature;
  while ( splitLines.nextFeature( aSplitFeature ) )
  {
    if ( feedback->isCanceled() )
    {
      break;
    }

    splitGeoms.insert( aSplitFeature.id(), aSplitFeature.geometry() );
    spatialIndex.addFeature( aSplitFeature );
  }

  QgsFeature outFeat;
  QgsFeatureIterator features = source->getFeatures();

  double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
  int i = 0;
  QgsFeature inFeatureA;
  while ( features.nextFeature( inFeatureA ) )
  {
    i++;
    if ( feedback->isCanceled() )
    {
      break;
    }

    if ( !inFeatureA.hasGeometry() )
    {
      sink->addFeature( inFeatureA, QgsFeatureSink::FastInsert );
      continue;
    }

    QgsGeometry inGeom = inFeatureA.geometry();
    outFeat.setAttributes( inFeatureA.attributes() );

    QVector< QgsGeometry > inGeoms = inGeom.asGeometryCollection();

    const QgsFeatureIds lines = spatialIndex.intersects( inGeom.boundingBox() ).toSet();
    if ( !lines.empty() ) // has intersection of bounding boxes
    {
      QVector< QgsGeometry > splittingLines;

      // use prepared geometries for faster intersection tests
      std::unique_ptr< QgsGeometryEngine > engine;

      for ( QgsFeatureId line : lines )
      {
        // check if trying to self-intersect
        if ( sameLayer && inFeatureA.id() == line )
          continue;

        QgsGeometry splitGeom = splitGeoms.value( line );
        if ( !engine )
        {
          engine.reset( QgsGeometry::createGeometryEngine( inGeom.constGet() ) );
          engine->prepareGeometry();
        }

        if ( engine->intersects( splitGeom.constGet() ) )
        {
          QVector< QgsGeometry > splitGeomParts = splitGeom.asGeometryCollection();
          splittingLines.append( splitGeomParts );
        }
      }

      if ( !splittingLines.empty() )
      {
        for ( const QgsGeometry &splitGeom : qgis::as_const( splittingLines ) )
        {
          QVector<QgsPointXY> splitterPList;
          QVector< QgsGeometry > outGeoms;

          // use prepared geometries for faster intersection tests
          std::unique_ptr< QgsGeometryEngine > splitGeomEngine( QgsGeometry::createGeometryEngine( splitGeom.constGet() ) );
          splitGeomEngine->prepareGeometry();
          while ( !inGeoms.empty() )
          {
            if ( feedback->isCanceled() )
            {
              break;
            }

            QgsGeometry inGeom = inGeoms.takeFirst();
            if ( !inGeom )
              continue;

            if ( splitGeomEngine->intersects( inGeom.constGet() ) )
            {
              QgsGeometry before = inGeom;
              if ( splitterPList.empty() )
              {
                const QgsCoordinateSequence sequence = splitGeom.constGet()->coordinateSequence();
                for ( const QgsRingSequence &part : sequence )
                {
                  for ( const QgsPointSequence &ring : part )
                  {
                    for ( const QgsPoint &pt : ring )
                    {
                      splitterPList << QgsPointXY( pt );
                    }
                  }
                }
              }

              QVector< QgsGeometry > newGeometries;
              QVector<QgsPointXY> topologyTestPoints;
              QgsGeometry::OperationResult result = inGeom.splitGeometry( splitterPList, newGeometries, false, topologyTestPoints );

              // splitGeometry: If there are several intersections
              // between geometry and splitLine, only the first one is considered.
              if ( result == QgsGeometry::Success ) // split occurred
              {
                if ( inGeom.isGeosEqual( before ) )
                {
                  // bug in splitGeometry: sometimes it returns 0 but
                  // the geometry is unchanged
                  outGeoms.append( inGeom );
                }
                else
                {
                  inGeoms.append( inGeom );
                  inGeoms.append( newGeometries );
                }
              }
              else
              {
                outGeoms.append( inGeom );
              }
            }
            else
            {
              outGeoms.append( inGeom );
            }

          }
          inGeoms = outGeoms;
        }
      }
    }

    QVector< QgsGeometry > parts;
    for ( const QgsGeometry &aGeom : qgis::as_const( inGeoms ) )
    {
      if ( feedback->isCanceled() )
      {
        break;
      }

      bool passed = true;
      if ( QgsWkbTypes::geometryType( aGeom.wkbType() ) == QgsWkbTypes::LineGeometry )
      {
        int numPoints = aGeom.constGet()->nCoordinates();

        if ( numPoints <= 2 )
        {
          if ( numPoints == 2 )
            passed = !static_cast< const QgsCurve * >( aGeom.constGet() )->isClosed(); // tests if vertex 0 = vertex 1
          else
            passed = false; // sometimes splitting results in lines of zero length
        }
      }

      if ( passed )
        parts.append( aGeom );
    }

    for ( const QgsGeometry &g : parts )
    {
      outFeat.setGeometry( g );
      sink->addFeature( outFeat, QgsFeatureSink::FastInsert );
    }

    feedback->setProgress( i * step );
  }

  QVariantMap outputs;
  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
  return outputs;
}
void QgsPointDisplacementRenderer::createDisplacementGroups( QgsVectorLayer* vlayer, const QgsRectangle& viewExtent )
{
  if ( !vlayer || ( vlayer->wkbType() != QGis::WKBPoint && vlayer->wkbType() != QGis::WKBPoint25D ) )
  {
    return;
  }

  mDisplacementGroups.clear();
  mDisplacementIds.clear();

  //use a spatial index to check if there is already a point at a position
  QgsSpatialIndex spatialIndex;

  //attributes
  QgsAttributeList attList;
  QList<QString> attributeStrings = usedAttributes();
  QList<QString>::const_iterator attStringIt = attributeStrings.constBegin();
  for ( ; attStringIt != attributeStrings.constEnd(); ++attStringIt )
  {
    attList.push_back( vlayer->fieldNameIndex( *attStringIt ) );
  }

  QgsFeature f;
  QList<QgsFeatureId> intersectList;

  //Because the new vector api does not allow querying features by id within a nextFeature loop, default constructed QgsFeature() is
  //inserted first and the real features are created in a second loop

  QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( viewExtent ).setSubsetOfAttributes( attList ) );
  while ( fit.nextFeature( f ) )
  {
    intersectList.clear();

    //check, if there is already a point at that position
    if ( f.geometry() )
    {
      intersectList = spatialIndex.intersects( searchRect( f.geometry()->asPoint() ) );
      if ( intersectList.empty() )
      {
        spatialIndex.insertFeature( f );
      }
      else
      {
        //go through all the displacement group maps and search an entry where the id equals the result of the spatial search
        QgsFeatureId existingEntry = intersectList.at( 0 );
        bool found = false;
        QList< QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin();
        for ( ; it != mDisplacementGroups.end(); ++it )
        {
          if ( it->size() > 0 && it->contains( existingEntry ) )
          {
            found = true;
            QgsFeature feature;
            it->insert( f.id(), QgsFeature() );
            mDisplacementIds.insert( f.id() );
            break;
          }
        }

        if ( !found )//insert the already existing feature and the new one into a map
        {
          QMap<QgsFeatureId, QgsFeature> newMap;
          newMap.insert( existingEntry, QgsFeature() );
          mDisplacementIds.insert( existingEntry );
          newMap.insert( f.id(), QgsFeature() );
          mDisplacementIds.insert( f.id() );
          mDisplacementGroups.push_back( newMap );
        }
      }
    }
  }

  //insert the real features into mDisplacementGroups
  QList< QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin();
  for ( ; it != mDisplacementGroups.end(); ++it )
  {
    QMap<QgsFeatureId, QgsFeature>::iterator mapIt = it->begin();
    for ( ; mapIt != it->end(); ++mapIt )
    {
      QgsFeature fet;
      vlayer->getFeatures( QgsFeatureRequest().setFilterFid( mapIt.key() ) ).nextFeature( fet );
      mapIt.value() = fet;
    }
  }

}
void QgsPointDisplacementRenderer::createDisplacementGroups( QgsVectorLayer* vlayer, const QgsRectangle& viewExtent )
{
  if ( !vlayer || ( vlayer->wkbType() != QGis::WKBPoint && vlayer->wkbType() != QGis::WKBPoint25D ) )
  {
    return;
  }

  mDisplacementGroups.clear();
  mDisplacementIds.clear();

  //use a spatial index to check if there is already a point at a position
  QgsSpatialIndex spatialIndex;

  //attributes
  QgsAttributeList attList;
  QList<QString> attributeStrings = usedAttributes();
  QList<QString>::const_iterator attStringIt = attributeStrings.constBegin();
  for ( ; attStringIt != attributeStrings.constEnd(); ++attStringIt )
  {
    attList.push_back( vlayer->fieldNameIndex( *attStringIt ) );
  }

  QgsFeature f;
  QList<int> intersectList;

  vlayer->select( attList, viewExtent, true, false );
  while ( vlayer->nextFeature( f ) )
  {
    intersectList.clear();

    //check, if there is already a point at that position
    if ( f.geometry() )
    {
      intersectList = spatialIndex.intersects( searchRect( f.geometry()->asPoint() ) );
      if ( intersectList.empty() )
      {
        spatialIndex.insertFeature( f );
      }
      else
      {
        //go through all the displacement group maps and search an entry where the id equals the result of the spatial search
        int existingEntry = intersectList.at( 0 );
        bool found = false;
        QList<QMap<int, QgsFeature> >::iterator it = mDisplacementGroups.begin();
        for ( ; it != mDisplacementGroups.end(); ++it )
        {
          if ( it->size() > 0 && it->contains( existingEntry ) )
          {
            found = true;
            QgsFeature feature;
            it->insert( f.id(), f );
            mDisplacementIds.insert( f.id() );
            break;
          }
        }

        if ( !found )//insert the already existing feature and the new one into a map
        {
          QMap<int, QgsFeature> newMap;
          QgsFeature existingFeature;
          vlayer->featureAtId( existingEntry, existingFeature );
          newMap.insert( existingEntry, existingFeature );
          mDisplacementIds.insert( existingEntry );
          newMap.insert( f.id(), f );
          mDisplacementIds.insert( f.id() );
          mDisplacementGroups.push_back( newMap );
        }
      }
    }
  }
  //refresh the selection because the vector layer is going to step through all features now
  vlayer->select( attList, viewExtent, true, false );
}