QgsFeatureIds QgsMapToolSelectUtils::getMatchingFeatures( QgsMapCanvas *canvas, const QgsGeometry &selectGeometry, bool doContains, bool singleSelect )
{
  QgsFeatureIds newSelectedFeatures;

  if ( selectGeometry.type() != QgsWkbTypes::PolygonGeometry )
    return newSelectedFeatures;

  QgsVectorLayer *vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas );
  if ( !vlayer )
    return newSelectedFeatures;

  // toLayerCoordinates will throw an exception for any 'invalid' points in
  // the rubber band.
  // For example, if you project a world map onto a globe using EPSG 2163
  // and then click somewhere off the globe, an exception will be thrown.
  QgsGeometry selectGeomTrans = selectGeometry;

  try
  {
    QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs(), QgsProject::instance() );

    if ( !ct.isShortCircuited() && selectGeomTrans.type() == QgsWkbTypes::PolygonGeometry )
    {
      // convert add more points to the edges of the rectangle
      // improve transformation result
      QgsPolygonXY poly( selectGeomTrans.asPolygon() );
      if ( poly.size() == 1 && poly.at( 0 ).size() == 5 )
      {
        const QgsPolylineXY &ringIn = poly.at( 0 );

        QgsPolygonXY newpoly( 1 );
        newpoly[0].resize( 41 );
        QgsPolylineXY &ringOut = newpoly[0];

        ringOut[ 0 ] = ringIn.at( 0 );

        int i = 1;
        for ( int j = 1; j < 5; j++ )
        {
          QgsVector v( ( ringIn.at( j ) - ringIn.at( j - 1 ) ) / 10.0 );
          for ( int k = 0; k < 9; k++ )
          {
            ringOut[ i ] = ringOut[ i - 1 ] + v;
            i++;
          }
          ringOut[ i++ ] = ringIn.at( j );
        }
        selectGeomTrans = QgsGeometry::fromPolygonXY( newpoly );
      }
    }

    selectGeomTrans.transform( ct );
  }
  catch ( QgsCsException &cse )
  {
    Q_UNUSED( cse );
    // catch exception for 'invalid' point and leave existing selection unchanged
    QgsDebugMsg( QStringLiteral( "Caught CRS exception " ) );
    QgisApp::instance()->messageBar()->pushMessage(
      QObject::tr( "CRS Exception" ),
      QObject::tr( "Selection extends beyond layer's coordinate system" ),
      Qgis::Warning,
      QgisApp::instance()->messageTimeout() );
    return newSelectedFeatures;
  }

  QgsDebugMsgLevel( "Selection layer: " + vlayer->name(), 3 );
  QgsDebugMsgLevel( "Selection polygon: " + selectGeomTrans.asWkt(), 3 );
  QgsDebugMsgLevel( "doContains: " + QString( doContains ? "T" : "F" ), 3 );

  QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() );
  context.expressionContext() << QgsExpressionContextUtils::layerScope( vlayer );
  std::unique_ptr< QgsFeatureRenderer > r;
  if ( vlayer->renderer() )
  {
    r.reset( vlayer->renderer()->clone() );
    r->startRender( context, vlayer->fields() );
  }

  QgsFeatureRequest request;
  request.setFilterRect( selectGeomTrans.boundingBox() );
  request.setFlags( QgsFeatureRequest::ExactIntersect );
  if ( r )
    request.setSubsetOfAttributes( r->usedAttributes( context ), vlayer->fields() );
  else
    request.setNoAttributes();

  QgsFeatureIterator fit = vlayer->getFeatures( request );

  QgsFeature f;
  QgsFeatureId closestFeatureId = 0;
  bool foundSingleFeature = false;
  double closestFeatureDist = std::numeric_limits<double>::max();
  while ( fit.nextFeature( f ) )
  {
    context.expressionContext().setFeature( f );
    // make sure to only use features that are visible
    if ( r && !r->willRenderFeature( f, context ) )
      continue;

    QgsGeometry g = f.geometry();
    if ( doContains )
    {
      if ( !selectGeomTrans.contains( g ) )
        continue;
    }
    else
    {
      if ( !selectGeomTrans.intersects( g ) )
        continue;
    }
    if ( singleSelect )
    {
      foundSingleFeature = true;
      double distance = g.distance( selectGeomTrans );
      if ( distance <= closestFeatureDist )
      {
        closestFeatureDist = distance;
        closestFeatureId = f.id();
      }
    }
    else
    {
      newSelectedFeatures.insert( f.id() );
    }
  }
  if ( singleSelect && foundSingleFeature )
  {
    newSelectedFeatures.insert( closestFeatureId );
  }

  if ( r )
    r->stopRender( context );

  QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) );

  return newSelectedFeatures;
}
Exemplo n.º 2
0
void QgsMapToolRotateFeature::canvasPressEvent( QMouseEvent * e )
{
  mRotation = 0;
  if ( mCtrl )
  {
    return;
  }

  delete mRubberBand;
  mRubberBand = 0;

  mInitialPos = e->pos();

  QgsVectorLayer* vlayer = currentVectorLayer();
  if ( !vlayer )
  {
    notifyNotVectorLayer();
    return;
  }

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

  QgsPoint layerCoords = toLayerCoordinates( vlayer, e->pos() );
  double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() );
  QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius,
                           layerCoords.x() + searchRadius, layerCoords.y() + searchRadius );

  if ( vlayer->selectedFeatureCount() == 0 )
  {
    QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setSubsetOfAttributes( QgsAttributeList() ) );

    //find the closest feature
    QgsGeometry* pointGeometry = QgsGeometry::fromPoint( layerCoords );
    if ( !pointGeometry )
    {
      return;
    }

    double minDistance = std::numeric_limits<double>::max();

    QgsFeature cf;
    QgsFeature f;
    while ( fit.nextFeature( f ) )
    {
      if ( f.geometry() )
      {
        double currentDistance = pointGeometry->distance( *f.geometry() );
        if ( currentDistance < minDistance )
        {
          minDistance = currentDistance;
          cf = f;
        }
      }

    }

    delete pointGeometry;

    if ( minDistance == std::numeric_limits<double>::max() )
    {
      return;
    }

    QgsRectangle bound = cf.geometry()->boundingBox();
    mStartPointMapCoords = toMapCoordinates( vlayer, bound.center() );

    if ( !mAnchorPoint )
    {
      mAnchorPoint = new QgsVertexMarker( mCanvas );
    }
    mAnchorPoint->setIconType( QgsVertexMarker::ICON_CROSS );
    mAnchorPoint->setCenter( mStartPointMapCoords );

    mStPoint = toCanvasCoordinates( mStartPointMapCoords );

    mRotatedFeatures.clear();
    mRotatedFeatures << cf.id(); //todo: take the closest feature, not the first one...

    mRubberBand = createRubberBand( vlayer->geometryType() );
    mRubberBand->setToGeometry( cf.geometry(), vlayer );
  }
  else
  {
    mRotatedFeatures = vlayer->selectedFeaturesIds();

    mRubberBand = createRubberBand( vlayer->geometryType() );

    QgsFeature feat;
    QgsFeatureIterator it = vlayer->selectedFeaturesIterator();
    while ( it.nextFeature( feat ) )
    {
      mRubberBand->addGeometry( feat.geometry(), vlayer );
    }
  }

  mRubberBand->setColor( QColor( 255, 0, 0, 65 ) );
  mRubberBand->setWidth( 2 );
  mRubberBand->show();

  double XDistance = mInitialPos.x() - mAnchorPoint->x();
  double YDistance = mInitialPos.y() - mAnchorPoint->y() ;
  mRotationOffset = atan2( YDistance, XDistance ) * ( 180 / PI );
}
Exemplo n.º 3
0
void QgsVectorDataProvider::fillMinMaxCache() const
{
  if ( !mCacheMinMaxDirty )
    return;

  QgsFields flds = fields();
  for ( int i = 0; i < flds.count(); ++i )
  {
    if ( flds.at( i ).type() == QVariant::Int )
    {
      mCacheMinValues[i] = QVariant( INT_MAX );
      mCacheMaxValues[i] = QVariant( INT_MIN );
    }
    else if ( flds.at( i ).type() == QVariant::LongLong )
    {
      mCacheMinValues[i] = QVariant( std::numeric_limits<qlonglong>::max() );
      mCacheMaxValues[i] = QVariant( std::numeric_limits<qlonglong>::min() );
    }
    else if ( flds.at( i ).type() == QVariant::Double )
    {
      mCacheMinValues[i] = QVariant( DBL_MAX );
      mCacheMaxValues[i] = QVariant( -DBL_MAX );
    }
    else
    {
      mCacheMinValues[i] = QVariant();
      mCacheMaxValues[i] = QVariant();
    }
  }

  QgsFeature f;
  QgsAttributeList keys = mCacheMinValues.keys();
  QgsFeatureIterator fi = getFeatures( QgsFeatureRequest().setSubsetOfAttributes( keys ) );

  while ( fi.nextFeature( f ) )
  {
    QgsAttributes attrs = f.attributes();
    for ( QgsAttributeList::const_iterator it = keys.begin(); it != keys.end(); ++it )
    {
      const QVariant& varValue = attrs.at( *it );

      if ( varValue.isNull() )
        continue;

      if ( flds.at( *it ).type() == QVariant::Int )
      {
        int value = varValue.toInt();
        if ( value < mCacheMinValues[*it].toInt() )
          mCacheMinValues[*it] = value;
        if ( value > mCacheMaxValues[*it].toInt() )
          mCacheMaxValues[*it] = value;
      }
      else if ( flds.at( *it ).type() == QVariant::LongLong )
      {
        qlonglong value = varValue.toLongLong();
        if ( value < mCacheMinValues[*it].toLongLong() )
          mCacheMinValues[*it] = value;
        if ( value > mCacheMaxValues[*it].toLongLong() )
          mCacheMaxValues[*it] = value;
      }
      else if ( flds.at( *it ).type() == QVariant::Double )
      {
        double value = varValue.toDouble();
        if ( value < mCacheMinValues[*it].toDouble() )
          mCacheMinValues[*it] = value;
        if ( value > mCacheMaxValues[*it].toDouble() )
          mCacheMaxValues[*it] = value;
      }
      else
      {
        QString value = varValue.toString();
        if ( mCacheMinValues[*it].isNull() || value < mCacheMinValues[*it].toString() )
        {
          mCacheMinValues[*it] = value;
        }
        if ( mCacheMaxValues[*it].isNull() || value > mCacheMaxValues[*it].toString() )
        {
          mCacheMaxValues[*it] = value;
        }
      }
    }
  }

  mCacheMinMaxDirty = false;
}
Exemplo n.º 4
0
void QgsGeometryCheckerSetupTab::runChecks()
{
  // Get selected layer
  QList<QgsVectorLayer *> layers = getSelectedLayers();
  if ( layers.isEmpty() )
    return;

  if ( ui.radioButtonOutputNew->isChecked() )
  {
    for ( QgsVectorLayer *layer : layers )
    {
      if ( layer->dataProvider()->dataSourceUri().startsWith( ui.lineEditOutputDirectory->text() ) )
      {
        QMessageBox::critical( this, tr( "Check Geometries" ), tr( "The chosen output directory contains one or more input layers." ) );
        return;
      }
    }
  }
  QgsVectorLayer *lineLayerCheckLayer = ui.comboLineLayerIntersection->isEnabled() ? dynamic_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( ui.comboLineLayerIntersection->currentData().toString() ) ) : nullptr;
  QgsVectorLayer *followBoundaryCheckLayer = ui.comboBoxFollowBoundaries->isEnabled() ? dynamic_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( ui.comboBoxFollowBoundaries->currentData().toString() ) ) : nullptr;
  if ( layers.contains( lineLayerCheckLayer ) || layers.contains( followBoundaryCheckLayer ) )
  {
    QMessageBox::critical( this, tr( "Check Geometries" ), tr( "The selected input layers cannot contain a layer also selected for a topology check." ) );
    return;
  }

  for ( QgsVectorLayer *layer : layers )
  {
    if ( layer->isEditable() )
    {
      QMessageBox::critical( this, tr( "Check Geometries" ), tr( "Input layer '%1' is not allowed to be in editing mode." ).arg( layer->name() ) );
      return;
    }
  }
  bool selectedOnly = ui.checkBoxInputSelectedOnly->isChecked();

  // Set window busy
  setCursor( Qt::WaitCursor );
  mRunButton->setEnabled( false );
  ui.labelStatus->setText( tr( "<b>Preparing output...</b>" ) );
  ui.labelStatus->show();
  QApplication::processEvents( QEventLoop::ExcludeUserInputEvents );

  QList<QgsVectorLayer *> processLayers;
  if ( ui.radioButtonOutputNew->isChecked() )
  {
    // Get output directory and file extension
    QDir outputDir = QDir( ui.lineEditOutputDirectory->text() );
    QString outputDriverName = ui.comboBoxOutputFormat->currentData().toString();
    QgsVectorFileWriter::MetaData metadata;
    if ( !QgsVectorFileWriter::driverMetadata( outputDriverName, metadata ) )
    {
      QMessageBox::critical( this, tr( "Check Geometries" ), tr( "The specified output format cannot be recognized." ) );
      mRunButton->setEnabled( true );
      ui.labelStatus->hide();
      unsetCursor();
      return;
    }
    QString outputExtension = metadata.ext;

    // List over input layers, check which existing project layers need to be removed and create output layers
    QString filenamePrefix = ui.lineEditFilenamePrefix->text();
    QSettings().setValue( "/geometry_checker/previous_values/filename_prefix", filenamePrefix );
    QStringList toRemove;
    QStringList createErrors;
    for ( QgsVectorLayer *layer : layers )
    {
      QString outputPath = outputDir.absoluteFilePath( filenamePrefix + layer->name() + "." + outputExtension );

      // Remove existing layer with same uri from project
      for ( QgsVectorLayer *projectLayer : QgsProject::instance()->layers<QgsVectorLayer *>() )
      {
        if ( projectLayer->dataProvider()->dataSourceUri().startsWith( outputPath ) )
        {
          toRemove.append( projectLayer->id() );
        }
      }

      // Create output layer
      QString errMsg;
      QgsVectorFileWriter::WriterError err =  QgsVectorFileWriter::writeAsVectorFormat( layer, outputPath, layer->dataProvider()->encoding(), layer->crs(), outputDriverName, selectedOnly, &errMsg );
      if ( err != QgsVectorFileWriter::NoError )
      {
        createErrors.append( errMsg );
        continue;
      }

      QgsVectorLayer *newlayer = new QgsVectorLayer( outputPath, QFileInfo( outputPath ).completeBaseName(), QStringLiteral( "ogr" ) );
      if ( selectedOnly )
      {
        QgsFeature feature;

        // Get features to select (only selected features were written up to this point)
        QgsFeatureIds selectedFeatures = newlayer->allFeatureIds();

        // Write non-selected feature ids
        QgsFeatureList features;
        QgsFeatureIterator it = layer->getFeatures();
        while ( it.nextFeature( feature ) )
        {
          if ( !layer->selectedFeatureIds().contains( feature.id() ) )
          {
            features.append( feature );
          }
        }
        newlayer->dataProvider()->addFeatures( features );

        // Set selected features
        newlayer->selectByIds( selectedFeatures );
      }
      processLayers.append( newlayer );
    }

    //  Remove layers from project
    if ( !toRemove.isEmpty() )
    {
      QgsProject::instance()->removeMapLayers( toRemove );
    }

    // Error if an output layer could not be created
    if ( !createErrors.isEmpty() )
    {
      QMessageBox::critical( this, tr( "Check Geometries" ), tr( "Failed to create one or more output layers:\n%1" ).arg( createErrors.join( "\n" ) ) );
      mRunButton->setEnabled( true );
      ui.labelStatus->hide();
      unsetCursor();
      return;
    }
  }
  else
  {
    processLayers = layers;
  }

  // Check if output layers are editable
  QList<QgsVectorLayer *> nonEditableLayers;
  for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) )
  {
    if ( ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeGeometries ) == 0 )
    {
      nonEditableLayers.append( layer );
    }
  }
  if ( !nonEditableLayers.isEmpty() )
  {
    QStringList nonEditableLayerNames;
    for ( QgsVectorLayer *layer : nonEditableLayers )
    {
      nonEditableLayerNames.append( layer->name() );
    }
    if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Check Geometries" ), tr( "The following output layers are in a format that does not support editing features:\n%1\n\nThe geometry check can be performed, but it will not be possible to fix any errors. Do you want to continue?" ).arg( nonEditableLayerNames.join( "\n" ) ), QMessageBox::Yes, QMessageBox::No ) )
    {
      if ( ui.radioButtonOutputNew->isChecked() )
      {
        for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) )
        {
          QString layerPath = layer->dataProvider()->dataSourceUri();
          delete layer;
          if ( ui.comboBoxOutputFormat->currentText() == QLatin1String( "ESRI Shapefile" ) )
          {
            QgsVectorFileWriter::deleteShapeFile( layerPath );
          }
          else
          {
            QFile( layerPath ).remove();
          }
        }
        mRunButton->setEnabled( true );
        ui.labelStatus->hide();
        unsetCursor();
      }
      return;
    }
  }

  // Setup checker
  ui.labelStatus->setText( tr( "<b>Building spatial index...</b>" ) );
  QApplication::processEvents( QEventLoop::ExcludeUserInputEvents );
  QMap<QString, QgsFeaturePool *> featurePools;
  for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) )
  {
    double layerToMapUntis = mIface->mapCanvas()->mapSettings().layerToMapUnits( layer );
    QgsCoordinateTransform layerToMapTransform( layer->crs(), QgsProject::instance()->crs(), QgsProject::instance() );
    featurePools.insert( layer->id(), new QgsFeaturePool( layer, layerToMapUntis, layerToMapTransform, selectedOnly ) );
  }
  // LineLayerIntersection check is enabled, make sure there is also a feature pool for that layer
  if ( ui.checkLineLayerIntersection->isChecked() && !featurePools.keys().contains( ui.comboLineLayerIntersection->currentData().toString() ) )
  {
    QgsVectorLayer *layer = dynamic_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( ui.comboLineLayerIntersection->currentData().toString() ) );
    Q_ASSERT( layer );
    double layerToMapUntis = mIface->mapCanvas()->mapSettings().layerToMapUnits( layer );
    QgsCoordinateTransform layerToMapTransform( layer->crs(), QgsProject::instance()->crs(), QgsProject::instance() );
    featurePools.insert( layer->id(), new QgsFeaturePool( layer, layerToMapUntis, layerToMapTransform, selectedOnly ) );
  }

  QgsGeometryCheckerContext *context = new QgsGeometryCheckerContext( ui.spinBoxTolerance->value(), QgsProject::instance()->crs(), featurePools );

  QList<QgsGeometryCheck *> checks;
  for ( const QgsGeometryCheckFactory *factory : QgsGeometryCheckFactoryRegistry::getCheckFactories() )
  {
    QgsGeometryCheck *check = factory->createInstance( context, ui );
    if ( check )
    {
      checks.append( check );
    }
  }
  QgsGeometryChecker *checker = new QgsGeometryChecker( checks, context );

  emit checkerStarted( checker );

  if ( ui.radioButtonOutputNew->isChecked() )
  {
    QList<QgsMapLayer *> addLayers;
    for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) )
    {
      addLayers.append( layer );
    }
    QgsProject::instance()->addMapLayers( addLayers );
  }

  // Run
  ui.buttonBox->addButton( mAbortButton, QDialogButtonBox::ActionRole );
  mRunButton->hide();
  ui.progressBar->setRange( 0, 0 );
  ui.labelStatus->hide();
  ui.progressBar->show();
  ui.widgetInputs->setEnabled( false );
  QEventLoop evLoop;
  QFutureWatcher<void> futureWatcher;
  connect( checker, &QgsGeometryChecker::progressValue, ui.progressBar, &QProgressBar::setValue );
  connect( &futureWatcher, &QFutureWatcherBase::finished, &evLoop, &QEventLoop::quit );
  connect( mAbortButton, &QAbstractButton::clicked, &futureWatcher, &QFutureWatcherBase::cancel );
  connect( mAbortButton, &QAbstractButton::clicked, this, &QgsGeometryCheckerSetupTab::showCancelFeedback );

  mIsRunningInBackground = true;

  int maxSteps = 0;
  futureWatcher.setFuture( checker->execute( &maxSteps ) );
  ui.progressBar->setRange( 0, maxSteps );
  evLoop.exec();

  mIsRunningInBackground = false;

  // Restore window
  unsetCursor();
  mAbortButton->setEnabled( true );
  ui.buttonBox->removeButton( mAbortButton );
  mRunButton->setEnabled( true );
  mRunButton->show();
  ui.progressBar->hide();
  ui.labelStatus->hide();
  ui.widgetInputs->setEnabled( true );

  // Show result
  emit checkerFinished( !futureWatcher.isCanceled() );
}
Exemplo n.º 5
0
bool QgsMapToolFeatureAction::doAction( QgsVectorLayer *layer, int x, int y )
{
  if ( !layer )
    return false;

  QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y );

  QgsFeatureList featList;

  // toLayerCoordinates will throw an exception for an 'invalid' point.
  // For example, if you project a world map onto a globe using EPSG 2163
  // and then click somewhere off the globe, an exception will be thrown.
  try
  {
    // create the search rectangle
    double searchRadius = searchRadiusMU( mCanvas );

    QgsRectangle r;
    r.setXMinimum( point.x() - searchRadius );
    r.setXMaximum( point.x() + searchRadius );
    r.setYMinimum( point.y() - searchRadius );
    r.setYMaximum( point.y() + searchRadius );

    r = toLayerCoordinates( layer, r );

    QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) );
    QgsFeature f;
    while ( fit.nextFeature( f ) )
      featList << QgsFeature( f );
  }
  catch ( QgsCsException & cse )
  {
    Q_UNUSED( cse );
    // catch exception for 'invalid' point and proceed with no features found
    QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
  }

  if ( featList.size() == 0 )
    return false;

  Q_FOREACH ( const QgsFeature& feat, featList )
  {
    if ( layer->actions()->defaultAction() >= 0 )
    {
      // define custom substitutions: layer id and clicked coords
      QMap<QString, QVariant> substitutionMap;
      substitutionMap.insert( "$layerid", layer->id() );
      point = toLayerCoordinates( layer, point );
      substitutionMap.insert( "$clickx", point.x() );
      substitutionMap.insert( "$clicky", point.y() );

      int actionIdx = layer->actions()->defaultAction();
      layer->actions()->doAction( actionIdx, feat, &substitutionMap );
    }
    else
    {
      QgsMapLayerAction* mapLayerAction = QgsMapLayerActionRegistry::instance()->defaultActionForLayer( layer );
      if ( mapLayerAction )
      {
        mapLayerAction->triggerForFeature( layer, &feat );
      }
    }
  }

  return true;
}
void QgsAtlasComposition::beginRender()
{
  if ( !mComposerMap || !mCoverageLayer )
  {
    return;
  }

  const QgsCoordinateReferenceSystem& coverage_crs = mCoverageLayer->crs();
  const QgsCoordinateReferenceSystem& destination_crs = mComposerMap->mapRenderer()->destinationCrs();
  // transformation needed for feature geometries
  mTransform.setSourceCrs( coverage_crs );
  mTransform.setDestCRS( destination_crs );

  const QgsFields& fields = mCoverageLayer->pendingFields();

  if ( !mSingleFile && mFilenamePattern.size() > 0 )
  {
    mFilenameExpr = std::auto_ptr<QgsExpression>( new QgsExpression( mFilenamePattern ) );
    // expression used to evaluate each filename
    // test for evaluation errors
    if ( mFilenameExpr->hasParserError() )
    {
      throw std::runtime_error( tr( "Filename parsing error: %1" ).arg( mFilenameExpr->parserErrorString() ).toLocal8Bit().data() );
    }

    // prepare the filename expression
    mFilenameExpr->prepare( fields );
  }

  // select all features with all attributes
  QgsFeatureIterator fit = mCoverageLayer->getFeatures();

  std::auto_ptr<QgsExpression> filterExpression;
  if ( mFilterFeatures )
  {
    filterExpression = std::auto_ptr<QgsExpression>( new QgsExpression( mFeatureFilter ) );
    if ( filterExpression->hasParserError() )
    {
      throw std::runtime_error( tr( "Feature filter parser error: %1" ).arg( filterExpression->parserErrorString() ).toLocal8Bit().data() );
    }
  }

  // We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process
  // We thus store the feature ids for future extraction
  QgsFeature feat;
  mFeatureIds.clear();
  mFeatureKeys.clear();
  while ( fit.nextFeature( feat ) )
  {
    if ( mFilterFeatures )
    {
      QVariant result = filterExpression->evaluate( &feat, mCoverageLayer->pendingFields() );
      if ( filterExpression->hasEvalError() )
      {
        throw std::runtime_error( tr( "Feature filter eval error: %1" ).arg( filterExpression->evalErrorString() ).toLocal8Bit().data() );
      }

      // skip this feature if the filter evaluation if false
      if ( !result.toBool() )
      {
        continue;
      }
    }
    mFeatureIds.push_back( feat.id() );

    if ( mSortFeatures )
    {
      mFeatureKeys.insert( std::make_pair( feat.id(), feat.attributes()[ mSortKeyAttributeIdx ] ) );
    }
  }

  // sort features, if asked for
  if ( mSortFeatures )
  {
    FieldSorter sorter( mFeatureKeys, mSortAscending );
    qSort( mFeatureIds.begin(), mFeatureIds.end(), sorter );
  }

  mOrigExtent = mComposerMap->extent();

  mRestoreLayer = false;
  QStringList& layerSet = mComposition->mapRenderer()->layerSet();
  if ( mHideCoverage )
  {
    // look for the layer in the renderer's set
    int removeAt = layerSet.indexOf( mCoverageLayer->id() );
    if ( removeAt != -1 )
    {
      mRestoreLayer = true;
      layerSet.removeAt( removeAt );
    }
  }

  // special columns for expressions
  QgsExpression::setSpecialColumn( "$numpages", QVariant( mComposition->numPages() ) );
  QgsExpression::setSpecialColumn( "$numfeatures", QVariant(( int )mFeatureIds.size() ) );
}
Exemplo n.º 7
0
void QgsDwgImportDialog::pbLoadDatabase_clicked()
{
  if ( !QFileInfo::exists( mDatabaseFileWidget->filePath() ) )
    return;

  QgsTemporaryCursorOverride waitCursor( Qt::BusyCursor );

  bool lblVisible = false;

  QgsVectorLayer::LayerOptions options { QgsProject::instance()->transformContext() };
  options.loadDefaultStyle = false;
  std::unique_ptr<QgsVectorLayer> d( new QgsVectorLayer( QStringLiteral( "%1|layername=drawing" ).arg( mDatabaseFileWidget->filePath() ), QStringLiteral( "layers" ), QStringLiteral( "ogr" ), options ) );
  if ( d && d->isValid() )
  {
    int idxPath = d->fields().lookupField( QStringLiteral( "path" ) );
    int idxLastModified = d->fields().lookupField( QStringLiteral( "lastmodified" ) );
    int idxCrs = d->fields().lookupField( QStringLiteral( "crs" ) );

    QgsFeature f;
    if ( d->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() << idxPath << idxLastModified << idxCrs ) ).nextFeature( f ) )
    {
      leDrawing->setText( f.attribute( idxPath ).toString() );

      QgsCoordinateReferenceSystem crs( f.attribute( idxCrs ).toInt(), QgsCoordinateReferenceSystem::InternalCrsId );
      mCrsSelector->setCrs( crs );
      mCrsSelector->setLayerCrs( crs );

      QFileInfo fi( leDrawing->text() );
      if ( fi.exists() )
      {
        if ( fi.lastModified() > f.attribute( idxLastModified ).toDateTime() )
        {
          lblMessage->setText( tr( "Drawing file was meanwhile updated (%1 > %2)." ).arg( fi.lastModified().toString(), f.attribute( idxLastModified ).toDateTime().toString() ) );
          lblVisible = true;
        }
      }
      else
      {
        lblMessage->setText( tr( "Drawing file unavailable." ) );
        lblVisible = true;
      }
    }
  }

  lblMessage->setVisible( lblVisible );

  std::unique_ptr<QgsVectorLayer> l( new QgsVectorLayer( QStringLiteral( "%1|layername=layers" ).arg( mDatabaseFileWidget->filePath() ), QStringLiteral( "layers" ), QStringLiteral( "ogr" ), options ) );
  if ( l && l->isValid() )
  {
    int idxName = l->fields().lookupField( QStringLiteral( "name" ) );
    int idxColor = l->fields().lookupField( QStringLiteral( "ocolor" ) );
    int idxFlags = l->fields().lookupField( QStringLiteral( "flags" ) );

    QgsDebugMsg( QStringLiteral( "idxName:%1 idxColor:%2 idxFlags:%3" ).arg( idxName ).arg( idxColor ).arg( idxFlags ) );

    QgsFeatureIterator fit = l->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() << idxName << idxColor << idxFlags ) );
    QgsFeature f;

    mLayers->setRowCount( 0 );

    while ( fit.nextFeature( f ) )
    {
      int row = mLayers->rowCount();
      mLayers->setRowCount( row + 1 );

      QgsDebugMsg( QStringLiteral( "name:%1 color:%2 flags:%3" ).arg( f.attribute( idxName ).toString() ).arg( f.attribute( idxColor ).toInt() ).arg( f.attribute( idxFlags ).toString(), 0, 16 ) );

      QTableWidgetItem *item = nullptr;
      item = new QTableWidgetItem( f.attribute( idxName ).toString() );
      item->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
      item->setCheckState( Qt::Checked );
      mLayers->setItem( row, 0, item );

      item = new QTableWidgetItem();
      item->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
      item->setCheckState( ( f.attribute( idxColor ).toInt() >= 0 && ( f.attribute( idxFlags ).toInt() & 1 ) == 0 ) ? Qt::Checked : Qt::Unchecked );
      mLayers->setItem( row, 1, item );
    }

    mLayers->resizeColumnsToContents();

    buttonBox->button( QDialogButtonBox::Ok )->setEnabled( mLayers->rowCount() > 0 && !leLayerGroup->text().isEmpty() );
  }
  else
  {
    bar->pushMessage( tr( "Could not open layer list" ), Qgis::Critical, 4 );
  }
}
Exemplo n.º 8
0
bool QgsMapToolFeatureAction::doAction( QgsVectorLayer *layer, int x, int y )
{
  if ( !layer )
    return false;

  QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y );

  QgsRectangle r;

  // create the search rectangle
  double searchRadius = searchRadiusMU( mCanvas );

  r.setXMinimum( point.x() - searchRadius );
  r.setXMaximum( point.x() + searchRadius );
  r.setYMinimum( point.y() - searchRadius );
  r.setYMaximum( point.y() + searchRadius );

  // toLayerCoordinates will throw an exception for an 'invalid' point.
  // For example, if you project a world map onto a globe using EPSG 2163
  // and then click somewhere off the globe, an exception will be thrown.
  try
  {
    r = toLayerCoordinates( layer, r );
  }
  catch ( QgsCsException & cse )
  {
    Q_UNUSED( cse );
    // catch exception for 'invalid' point and proceed with no features found
    QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
  }

  QgsAction defaultAction = layer->actions()->defaultAction( QStringLiteral( "Canvas" ) );

  QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) );
  QgsFeature feat;
  while ( fit.nextFeature( feat ) )
  {
    if ( defaultAction.isValid() )
    {
      // define custom substitutions: layer id and clicked coords
      QgsExpressionContext context;
      context << QgsExpressionContextUtils::globalScope()
      << QgsExpressionContextUtils::projectScope()
      << QgsExpressionContextUtils::mapSettingsScope( mCanvas->mapSettings() );
      QgsExpressionContextScope* actionScope = new QgsExpressionContextScope();
      actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "click_x" ), point.x(), true ) );
      actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "click_y" ), point.y(), true ) );
      actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "action_scope" ), QStringLiteral( "Canvas" ), true ) );
      context << actionScope;

      defaultAction.run( layer, feat, context );
    }
    else
    {
      QgsMapLayerAction* mapLayerAction = QgsMapLayerActionRegistry::instance()->defaultActionForLayer( layer );
      if ( mapLayerAction )
      {
        mapLayerAction->triggerForFeature( layer, &feat );
      }
    }
  }

  return true;
}
Exemplo n.º 9
0
  QDomDocument createTransactionDocument( QgsServerInterface* serverIface, const QString& version,
                                          const QgsServerRequest& request )
  {
    Q_UNUSED( version );

    QDomDocument doc;

    QgsWfsProjectParser* configParser = getConfigParser( serverIface );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
    QgsAccessControl* accessControl = serverIface->accessControls();
#endif
    const QString requestBody = request.getParameter( QStringLiteral( "REQUEST_BODY" ) );

    QString errorMsg;
    if ( !doc.setContent( requestBody, true, &errorMsg ) )
    {
      throw QgsRequestNotWellFormedException( errorMsg );
    }

    QDomElement docElem = doc.documentElement();
    QDomNodeList docChildNodes = docElem.childNodes();

    // Re-organize the transaction document
    QDomDocument mDoc;
    QDomElement mDocElem = mDoc.createElement( QStringLiteral( "myTransactionDocument" ) );
    mDocElem.setAttribute( QStringLiteral( "xmlns" ), QGS_NAMESPACE );
    mDocElem.setAttribute( QStringLiteral( "xmlns:wfs" ), WFS_NAMESPACE );
    mDocElem.setAttribute( QStringLiteral( "xmlns:gml" ), GML_NAMESPACE );
    mDocElem.setAttribute( QStringLiteral( "xmlns:ogc" ), OGC_NAMESPACE );
    mDocElem.setAttribute( QStringLiteral( "xmlns:qgs" ), QGS_NAMESPACE );
    mDocElem.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
    mDoc.appendChild( mDocElem );

    QDomElement actionElem;
    QString actionName;
    QDomElement typeNameElem;
    QString typeName;

    for ( int i = docChildNodes.count(); 0 < i; --i )
    {
      actionElem = docChildNodes.at( i - 1 ).toElement();
      actionName = actionElem.localName();

      if ( actionName == QLatin1String( "Insert" ) )
      {
        QDomElement featureElem = actionElem.firstChild().toElement();
        typeName = featureElem.localName();
      }
      else if ( actionName == QLatin1String( "Update" ) )
      {
        typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
      }
      else if ( actionName == QLatin1String( "Delete" ) )
      {
        typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
      }

      if ( typeName.contains( QLatin1String( ":" ) ) )
        typeName = typeName.section( QStringLiteral( ":" ), 1, 1 );

      QDomNodeList typeNameList = mDocElem.elementsByTagName( typeName );
      if ( typeNameList.count() == 0 )
      {
        typeNameElem = mDoc.createElement( typeName );
        mDocElem.appendChild( typeNameElem );
      }
      else
        typeNameElem = typeNameList.at( 0 ).toElement();

      typeNameElem.appendChild( actionElem );
    }

    // It's time to make the transaction
    // Create the response document
    QDomDocument resp;
    //wfs:WFS_TransactionRespone element
    QDomElement respElem = resp.createElement( QStringLiteral( "WFS_TransactionResponse" )/*wfs:WFS_TransactionResponse*/ );
    respElem.setAttribute( QStringLiteral( "xmlns" ), WFS_NAMESPACE );
    respElem.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
    respElem.setAttribute( QStringLiteral( "xsi:schemaLocation" ), WFS_NAMESPACE + " http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
    respElem.setAttribute( QStringLiteral( "xmlns:ogc" ), OGC_NAMESPACE );
    respElem.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.0.0" ) );
    resp.appendChild( respElem );

    // Store the created feature id for WFS
    QStringList insertResults;
    // Get the WFS layers id
    QStringList wfsLayersId = configParser->wfsLayers();;

    QList<QgsMapLayer*> layerList;
    QgsMapLayer* currentLayer = nullptr;

    // Loop through the layer transaction elements
    docChildNodes = mDocElem.childNodes();
    for ( int i = 0; i < docChildNodes.count(); ++i )
    {
      // Get the vector layer
      typeNameElem = docChildNodes.at( i ).toElement();
      typeName = typeNameElem.tagName();

      layerList = configParser->mapLayerFromTypeName( typeName );
      // Could be empty!
      if ( layerList.count() > 0 )
      {
        currentLayer = layerList.at( 0 );
      }
      else
      {
        throw QgsRequestNotWellFormedException( QStringLiteral( "Wrong TypeName: %1" ).arg( typeName ) );
      }

      QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( currentLayer );
      // it's a vectorlayer and defined by the administrator as a WFS layer
      if ( layer && wfsLayersId.contains( layer->id() ) )
      {
#ifdef HAVE_SERVER_PYTHON_PLUGINS
        if ( actionName == QLatin1String( "Insert" ) )
        {
          if ( !accessControl->layerInsertPermission( layer ) )
          {
            throw QgsSecurityAccessException( QStringLiteral( "Feature insert permission denied" ) );
          }
        }
        else if ( actionName == QLatin1String( "Update" ) )
        {
          if ( !accessControl->layerUpdatePermission( layer ) )
          {
            throw QgsSecurityAccessException( QStringLiteral( "Feature update permission denied" ) );
          }
        }
        else if ( actionName == QLatin1String( "Delete" ) )
        {
          if ( !accessControl->layerDeletePermission( layer ) )
          {
            throw QgsSecurityAccessException( QStringLiteral( "Feature delete permission denied" ) );
          }
        }
#endif

        // Get the provider and it's capabilities
        QgsVectorDataProvider* provider = layer->dataProvider();
        if ( !provider )
        {
          continue;
        }

        int cap = provider->capabilities();

        // Start the update transaction
        layer->startEditing();
        if (( cap & QgsVectorDataProvider::ChangeAttributeValues ) && ( cap & QgsVectorDataProvider::ChangeGeometries ) )
        {
          // Loop through the update elements for this layer
          QDomNodeList upNodeList = typeNameElem.elementsByTagNameNS( WFS_NAMESPACE, QStringLiteral( "Update" ) );
          for ( int j = 0; j < upNodeList.count(); ++j )
          {
            if ( !configParser->wfstUpdateLayers().contains( layer->id() ) )
            {
              //no wfs permissions to do updates
              QString errorMsg = "No permissions to do WFS updates on layer '" + layer->name() + "'";
              QgsMessageLog::logMessage( errorMsg, QStringLiteral( "Server" ), QgsMessageLog::CRITICAL );
              addTransactionResult( resp, respElem, QStringLiteral( "FAILED" ), QStringLiteral( "Update" ), errorMsg );
              return resp;
            }

            actionElem = upNodeList.at( j ).toElement();

            // Get the Feature Ids for this filter on the layer
            QDomElement filterElem = actionElem.elementsByTagName( QStringLiteral( "Filter" ) ).at( 0 ).toElement();
            QgsFeatureIds fids = getFeatureIdsFromFilter( filterElem, layer );

            // Loop through the property elements
            // Store properties and the geometry element
            QDomNodeList propertyNodeList = actionElem.elementsByTagName( QStringLiteral( "Property" ) );
            QMap<QString, QString> propertyMap;
            QDomElement propertyElem;
            QDomElement nameElem;
            QDomElement valueElem;
            QDomElement geometryElem;

            for ( int l = 0; l < propertyNodeList.count(); ++l )
            {
              propertyElem = propertyNodeList.at( l ).toElement();
              nameElem = propertyElem.elementsByTagName( QStringLiteral( "Name" ) ).at( 0 ).toElement();
              valueElem = propertyElem.elementsByTagName( QStringLiteral( "Value" ) ).at( 0 ).toElement();
              if ( nameElem.text() != QLatin1String( "geometry" ) )
              {
                propertyMap.insert( nameElem.text(), valueElem.text() );
              }
              else
              {
                geometryElem = valueElem;
              }
            }

            // Update the features
            QgsFields fields = provider->fields();
            QMap<QString, int> fieldMap = provider->fieldNameMap();
            QMap<QString, int>::const_iterator fieldMapIt;
            QString fieldName;
            bool conversionSuccess;

            QgsFeatureIds::const_iterator fidIt = fids.constBegin();
            for ( ; fidIt != fids.constEnd(); ++fidIt )
            {
#ifdef HAVE_SERVER_PYTHON_PLUGINS
              QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest( *fidIt ) );
              QgsFeature feature;
              while ( fit.nextFeature( feature ) )
              {
                if ( !accessControl->allowToEdit( layer, feature ) )
                {
                  throw QgsSecurityAccessException( QStringLiteral( "Feature modify permission denied" ) );
                }
              }
#endif

              QMap< QString, QString >::const_iterator it = propertyMap.constBegin();
              for ( ; it != propertyMap.constEnd(); ++it )
              {
                fieldName = it.key();
                fieldMapIt = fieldMap.find( fieldName );
                if ( fieldMapIt == fieldMap.constEnd() )
                {
                  continue;
                }
                QgsField field = fields.at( fieldMapIt.value() );
                if ( field.type() == 2 )
                  layer->changeAttributeValue( *fidIt, fieldMapIt.value(), it.value().toInt( &conversionSuccess ) );
                else if ( field.type() == 6 )
                  layer->changeAttributeValue( *fidIt, fieldMapIt.value(), it.value().toDouble( &conversionSuccess ) );
                else
                  layer->changeAttributeValue( *fidIt, fieldMapIt.value(), it.value() );
              }

              if ( !geometryElem.isNull() )
              {
                QgsGeometry g = QgsOgcUtils::geometryFromGML( geometryElem );
                if ( !layer->changeGeometry( *fidIt, g ) )
                {
                  throw QgsRequestNotWellFormedException( QStringLiteral( "Error in change geometry" ) );
                }
              }

#ifdef HAVE_SERVER_PYTHON_PLUGINS
              fit = layer->getFeatures( QgsFeatureRequest( *fidIt ) );
              while ( fit.nextFeature( feature ) )
              {
                if ( !accessControl->allowToEdit( layer, feature ) )
                {
                  layer->rollBack();
                  throw QgsSecurityAccessException( QStringLiteral( "Feature modify permission denied" ) );
                }
              }
#endif
            }
          }
        }
        // Commit the changes of the update elements
        if ( !layer->commitChanges() )
        {
          addTransactionResult( resp, respElem, QStringLiteral( "PARTIAL" ), QStringLiteral( "Update" ), layer->commitErrors().join( QStringLiteral( "\n  " ) ) );
          return resp;
        }
        // Start the delete transaction
        layer->startEditing();
        if (( cap & QgsVectorDataProvider::DeleteFeatures ) )
        {
          // Loop through the delete elements
          QDomNodeList delNodeList = typeNameElem.elementsByTagNameNS( WFS_NAMESPACE, QStringLiteral( "Delete" ) );
          for ( int j = 0; j < delNodeList.count(); ++j )
          {
            if ( !configParser->wfstDeleteLayers().contains( layer->id() ) )
            {
              //no wfs permissions to do updates
              QString errorMsg = "No permissions to do WFS deletes on layer '" + layer->name() + "'";
              QgsMessageLog::logMessage( errorMsg, QStringLiteral( "Server" ), QgsMessageLog::CRITICAL );
              addTransactionResult( resp, respElem, QStringLiteral( "FAILED" ), QStringLiteral( "Delete" ), errorMsg );
              return resp;
            }

            actionElem = delNodeList.at( j ).toElement();
            QDomElement filterElem = actionElem.firstChild().toElement();
            // Get Feature Ids for the Filter element
            QgsFeatureIds fids = getFeatureIdsFromFilter( filterElem, layer );

#ifdef HAVE_SERVER_PYTHON_PLUGINS
            QgsFeatureIds::const_iterator fidIt = fids.constBegin();
            for ( ; fidIt != fids.constEnd(); ++fidIt )
            {
              QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest( *fidIt ) );
              QgsFeature feature;
              while ( fit.nextFeature( feature ) )
              {
                if ( !accessControl->allowToEdit( layer, feature ) )
                {
                  throw QgsSecurityAccessException( QStringLiteral( "Feature modify permission denied" ) );
                }
              }
            }
#endif

            layer->selectByIds( fids );
            layer->deleteSelectedFeatures();
          }
        }
        // Commit the changes of the delete elements
        if ( !layer->commitChanges() )
        {
          addTransactionResult( resp, respElem, QStringLiteral( "PARTIAL" ), QStringLiteral( "Delete" ), layer->commitErrors().join( QStringLiteral( "\n  " ) ) );
          return resp;
        }

        // Store the inserted features
        QgsFeatureList inFeatList;
        if ( cap & QgsVectorDataProvider::AddFeatures )
        {
          // Get Layer Field Information
          QgsFields fields = provider->fields();
          QMap<QString, int> fieldMap = provider->fieldNameMap();
          QMap<QString, int>::const_iterator fieldMapIt;

          // Loop through the insert elements
          QDomNodeList inNodeList = typeNameElem.elementsByTagNameNS( WFS_NAMESPACE, QStringLiteral( "Insert" ) );
          for ( int j = 0; j < inNodeList.count(); ++j )
          {
            if ( !configParser->wfstInsertLayers().contains( layer->id() ) )
            {
              //no wfs permissions to do updates
              QString errorMsg = "No permissions to do WFS inserts on layer '" + layer->name() + "'";
              QgsMessageLog::logMessage( errorMsg, QStringLiteral( "Server" ), QgsMessageLog::CRITICAL );
              addTransactionResult( resp, respElem, QStringLiteral( "FAILED" ), QStringLiteral( "Insert" ), errorMsg );
              return resp;
            }

            actionElem = inNodeList.at( j ).toElement();
            // Loop through the feature element
            QDomNodeList featNodes = actionElem.childNodes();
            for ( int l = 0; l < featNodes.count(); l++ )
            {
              // Add the feature to the layer
              // and store it to put it's Feature Id in the response
              inFeatList << QgsFeature( fields );

              // Create feature for this layer
              QDomElement featureElem = featNodes.at( l ).toElement();

              QDomNode currentAttributeChild = featureElem.firstChild();

              while ( !currentAttributeChild.isNull() )
              {
                QDomElement currentAttributeElement = currentAttributeChild.toElement();
                QString attrName = currentAttributeElement.localName();

                if ( attrName != QLatin1String( "boundedBy" ) )
                {
                  if ( attrName != QLatin1String( "geometry" ) ) //a normal attribute
                  {
                    fieldMapIt = fieldMap.find( attrName );
                    if ( fieldMapIt == fieldMap.constEnd() )
                    {
                      continue;
                    }
                    QgsField field = fields.at( fieldMapIt.value() );
                    QString attrValue = currentAttributeElement.text();
                    int attrType = field.type();
                    QgsMessageLog::logMessage( QStringLiteral( "attr: name=%1 idx=%2 value=%3" ).arg( attrName ).arg( fieldMapIt.value() ).arg( attrValue ) );
                    if ( attrType == QVariant::Int )
                      inFeatList.last().setAttribute( fieldMapIt.value(), attrValue.toInt() );
                    else if ( attrType == QVariant::Double )
                      inFeatList.last().setAttribute( fieldMapIt.value(), attrValue.toDouble() );
                    else
                      inFeatList.last().setAttribute( fieldMapIt.value(), attrValue );
                  }
                  else //a geometry attribute
                  {
                    QgsGeometry g = QgsOgcUtils::geometryFromGML( currentAttributeElement );
                    inFeatList.last().setGeometry( g );
                  }
                }
                currentAttributeChild = currentAttributeChild.nextSibling();
              }
            }
          }
        }
#ifdef HAVE_SERVER_PYTHON_PLUGINS
        QgsFeatureList::iterator featureIt = inFeatList.begin();
        while ( featureIt != inFeatList.end() )
        {
          if ( !accessControl->allowToEdit( layer, *featureIt ) )
          {
            throw QgsSecurityAccessException( QStringLiteral( "Feature modify permission denied" ) );
          }
          featureIt++;
        }
#endif

        // add the features
        if ( !provider->addFeatures( inFeatList ) )
        {
          addTransactionResult( resp, respElem, QStringLiteral( "Partial" ), QStringLiteral( "Insert" ), layer->commitErrors().join( QStringLiteral( "\n  " ) ) );
          if ( provider->hasErrors() )
          {
            provider->clearErrors();
          }
          return resp;
        }
        // Get the Feature Ids of the inserted feature
        for ( int j = 0; j < inFeatList.size(); j++ )
        {
          insertResults << typeName + "." + QString::number( inFeatList[j].id() );
        }
      }
    }

    // Put the Feature Ids of the inserted feature
    if ( !insertResults.isEmpty() )
    {
      Q_FOREACH ( const QString &fidStr, insertResults )
      {
        QDomElement irElem = doc.createElement( QStringLiteral( "InsertResult" ) );
        QDomElement fiElem = doc.createElement( QStringLiteral( "ogc:FeatureId" ) );
        fiElem.setAttribute( QStringLiteral( "fid" ), fidStr );
        irElem.appendChild( fiElem );
        respElem.appendChild( irElem );
      }
Exemplo n.º 10
0
int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p )
{
  if ( !mLayer->isSpatial() )
    return 1;

  double segmentSearchEpsilon = mLayer->crs().isGeographic() ? 1e-12 : 1e-8;

  //work with a tolerance because coordinate projection may introduce some rounding
  double threshold = 0.0000001;
  if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceMeters )
  {
    threshold = 0.001;
  }
  else if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceFeet )
  {
    threshold = 0.0001;
  }

  QgsRectangle searchRect( p.x() - threshold, p.y() - threshold,
                           p.x() + threshold, p.y() + threshold );
  double sqrSnappingTolerance = threshold * threshold;

  QgsFeature f;
  QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest()
                           .setFilterRect( searchRect )
                           .setFlags( QgsFeatureRequest::ExactIntersect )
                           .setSubsetOfAttributes( QgsAttributeList() ) );

  QMap<QgsFeatureId, QgsGeometry> features;
  QMap<QgsFeatureId, int> segments;

  while ( fit.nextFeature( f ) )
  {
    int afterVertex;
    QgsPointXY snappedPoint;
    double sqrDistSegmentSnap = f.geometry().closestSegmentWithContext( p, snappedPoint, afterVertex, nullptr, segmentSearchEpsilon );
    if ( sqrDistSegmentSnap < sqrSnappingTolerance )
    {
      segments[f.id()] = afterVertex;
      features[f.id()] = f.geometry();
    }
  }

  if ( segments.isEmpty() )
    return 2;

  for ( QMap<QgsFeatureId, int>::const_iterator it = segments.constBegin(); it != segments.constEnd(); ++it )
  {
    QgsFeatureId fid = it.key();
    int segmentAfterVertex = it.value();
    QgsGeometry geom = features[fid];

    int atVertex, beforeVertex, afterVertex;
    double sqrDistVertexSnap;
    geom.closestVertex( p, atVertex, beforeVertex, afterVertex, sqrDistVertexSnap );

    if ( sqrDistVertexSnap < sqrSnappingTolerance )
      continue;  // the vertex already exists - do not insert it

    if ( !mLayer->insertVertex( p.x(), p.y(), fid, segmentAfterVertex ) )
    {
      QgsDebugMsg( "failed to insert topo point" );
    }
  }

  return 0;
}
Exemplo n.º 11
0
QVariantMap QgsCollectorAlgorithm::processCollection( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback,
    const std::function<QgsGeometry( const QVector< QgsGeometry >& )> &collector, int maxQueueLength )
{
  std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
  if ( !source )
    throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );

  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" ) ) );

  QStringList fields = parameterAsFields( parameters, QStringLiteral( "FIELD" ), context );

  long count = source->featureCount();

  QgsFeature f;
  QgsFeatureIterator it = source->getFeatures();

  double step = count > 0 ? 100.0 / count : 1;
  int current = 0;

  if ( fields.isEmpty() )
  {
    // dissolve all - not using fields
    bool firstFeature = true;
    // we dissolve geometries in blocks using unaryUnion
    QVector< QgsGeometry > geomQueue;
    QgsFeature outputFeature;

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

      if ( firstFeature )
      {
        outputFeature = f;
        firstFeature = false;
      }

      if ( f.hasGeometry() && !f.geometry().isNull() )
      {
        geomQueue.append( f.geometry() );
        if ( maxQueueLength > 0 && geomQueue.length() > maxQueueLength )
        {
          // queue too long, combine it
          QgsGeometry tempOutputGeometry = collector( geomQueue );
          geomQueue.clear();
          geomQueue << tempOutputGeometry;
        }
      }

      feedback->setProgress( current * step );
      current++;
    }

    outputFeature.setGeometry( collector( geomQueue ) );
    sink->addFeature( outputFeature, QgsFeatureSink::FastInsert );
  }
  else
  {
    QList< int > fieldIndexes;
    Q_FOREACH ( const QString &field, fields )
    {
      int index = source->fields().lookupField( field );
      if ( index >= 0 )
        fieldIndexes << index;
    }

    QHash< QVariant, QgsAttributes > attributeHash;
    QHash< QVariant, QVector< QgsGeometry > > geometryHash;

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

      QVariantList indexAttributes;
      Q_FOREACH ( int index, fieldIndexes )
      {
        indexAttributes << f.attribute( index );
      }

      if ( !attributeHash.contains( indexAttributes ) )
      {
        // keep attributes of first feature
        attributeHash.insert( indexAttributes, f.attributes() );
      }

      if ( f.hasGeometry() && !f.geometry().isNull() )
      {
        geometryHash[ indexAttributes ].append( f.geometry() );
      }
    }
Exemplo n.º 12
0
QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitParts( const QVector<QgsPointXY> &splitLine, bool topologicalEditing )
{
  if ( !mLayer->isSpatial() )
    return QgsGeometry::InvalidBaseGeometry;

  double xMin, yMin, xMax, yMax;
  QgsRectangle bBox; //bounding box of the split line
  QgsGeometry::OperationResult returnCode = QgsGeometry::OperationResult::Success;
  QgsGeometry::OperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry
  int numberOfSplitParts = 0;

  QgsFeatureIterator fit;

  if ( mLayer->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection
  {
    fit = mLayer->getSelectedFeatures();
  }
  else //else consider all the feature that intersect the bounding box of the split line
  {
    if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) )
    {
      bBox.setXMinimum( xMin );
      bBox.setYMinimum( yMin );
      bBox.setXMaximum( xMax );
      bBox.setYMaximum( yMax );
    }
    else
    {
      return QgsGeometry::OperationResult::InvalidInputGeometryType;
    }

    if ( bBox.isEmpty() )
    {
      //if the bbox is a line, try to make a square out of it
      if ( bBox.width() == 0.0 && bBox.height() > 0 )
      {
        bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
        bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
      }
      else if ( bBox.height() == 0.0 && bBox.width() > 0 )
      {
        bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
        bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
      }
      else
      {
        //If we have a single point, we still create a non-null box
        double bufferDistance = 0.000001;
        if ( mLayer->crs().isGeographic() )
          bufferDistance = 0.00000001;
        bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
        bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
        bBox.setYMinimum( bBox.yMinimum() - bufferDistance );
        bBox.setYMaximum( bBox.yMaximum() + bufferDistance );
      }
    }

    fit = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
  }

  QgsGeometry::OperationResult addPartRet = QgsGeometry::OperationResult::Success;

  QgsFeature feat;
  while ( fit.nextFeature( feat ) )
  {
    QVector<QgsGeometry> newGeometries;
    QVector<QgsPointXY> topologyTestPoints;
    QgsGeometry featureGeom = feat.geometry();
    splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
    if ( splitFunctionReturn == 0 )
    {
      //add new parts
      if ( !newGeometries.isEmpty() )
        featureGeom.convertToMultiType();

      for ( int i = 0; i < newGeometries.size(); ++i )
      {
        addPartRet = featureGeom.addPart( newGeometries.at( i ) );
        if ( addPartRet )
          break;
      }

      // For test only: Exception already thrown here...
      // feat.geometry()->asWkb();

      if ( !addPartRet )
      {
        mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom );
      }

      if ( topologicalEditing )
      {
        QVector<QgsPointXY>::const_iterator topol_it = topologyTestPoints.constBegin();
        for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
        {
          addTopologicalPoints( *topol_it );
        }
      }
      ++numberOfSplitParts;
    }
    else if ( splitFunctionReturn != QgsGeometry::OperationResult::Success && splitFunctionReturn != QgsGeometry::OperationResult::NothingHappened )
    {
      returnCode = splitFunctionReturn;
    }
  }

  if ( numberOfSplitParts == 0 && mLayer->selectedFeatureCount() > 0  && returnCode == QgsGeometry::Success )
  {
    //There is a selection but no feature has been split.
    //Maybe user forgot that only the selected features are split
    returnCode = QgsGeometry::OperationResult::NothingHappened;
  }

  return returnCode;
}
Exemplo n.º 13
0
void QgsMapToolFillRing::canvasReleaseEvent( QMouseEvent * e )
{
  //check if we operate on a vector layer
  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );

  if ( !vlayer )
  {
    notifyNotVectorLayer();
    return;
  }

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

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

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

    closePolygon();

    vlayer->beginEditCommand( tr( "Ring added and filled" ) );
    int addRingReturnCode = vlayer->addRing( points() );
    if ( addRingReturnCode != 0 )
    {
      QString errorMessage;
      //todo: open message box to communicate errors
      if ( addRingReturnCode == 1 )
      {
        errorMessage = tr( "a problem with geometry type occured" );
      }
      else if ( addRingReturnCode == 2 )
      {
        errorMessage = tr( "the inserted Ring is not closed" );
      }
      else if ( addRingReturnCode == 3 )
      {
        errorMessage = tr( "the inserted Ring is not a valid geometry" );
      }
      else if ( addRingReturnCode == 4 )
      {
        errorMessage = tr( "the inserted Ring crosses existing rings" );
      }
      else if ( addRingReturnCode == 5 )
      {
        errorMessage = tr( "the inserted Ring is not contained in a feature" );
      }
      else
      {
        errorMessage = tr( "an unknown error occured" );
      }
      emit messageEmitted( tr( "could not add ring since %1." ).arg( errorMessage ), QgsMessageBar::CRITICAL );
      vlayer->destroyEditCommand();
    }
    else
    {
      // find parent feature and get it attributes
      double xMin, xMax, yMin, yMax;
      QgsRectangle bBox;

      xMin = std::numeric_limits<double>::max();
      xMax = -std::numeric_limits<double>::max();
      yMin = std::numeric_limits<double>::max();
      yMax = -std::numeric_limits<double>::max();

      for ( QList<QgsPoint>::const_iterator it = points().constBegin(); it != points().constEnd(); ++it )
      {
        if ( it->x() < xMin )
        {
          xMin = it->x();
        }
        if ( it->x() > xMax )
        {
          xMax = it->x();
        }
        if ( it->y() < yMin )
        {
          yMin = it->y();
        }
        if ( it->y() > yMax )
        {
          yMax = it->y();
        }
      }
      bBox.setXMinimum( xMin );
      bBox.setYMinimum( yMin );
      bBox.setXMaximum( xMax );
      bBox.setYMaximum( yMax );

      QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );

      QgsFeature f;
      bool res = false;
      while ( fit.nextFeature( f ) )
      {
        //create QgsFeature with wkb representation
        QgsFeature* ft = new QgsFeature( vlayer->pendingFields(),  0 );

        QgsGeometry *g;
        g = QgsGeometry::fromPolygon( QgsPolygon() << points().toVector() );
        ft->setGeometry( g );
        ft->setAttributes( f.attributes() );

        if ( QgsApplication::keyboardModifiers() == Qt::ControlModifier )
        {
          res = vlayer->addFeature( *ft );
        }
        else
        {
          QgsAttributeDialog *dialog = new QgsAttributeDialog( vlayer, ft, false, NULL, true );
          dialog->setIsAddDialog( true );
          res = dialog->exec(); // will also add the feature
        }

        if ( res )
        {
          vlayer->endEditCommand();
        }
        else
        {
          delete ft;
          vlayer->destroyEditCommand();
        }
        res = false;
      }
    }
    stopCapturing();
  }
}
Exemplo n.º 14
0
int QgsAtlasComposition::updateFeatures()
{
  //needs to be called when layer, filter, sort changes

  if ( !mCoverageLayer )
  {
    return 0;
  }

  QgsExpressionContext expressionContext = createExpressionContext();

  updateFilenameExpression();

  // select all features with all attributes
  QgsFeatureRequest req;

  QScopedPointer<QgsExpression> filterExpression;
  if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
  {
    filterExpression.reset( new QgsExpression( mFeatureFilter ) );
    if ( filterExpression->hasParserError() )
    {
      mFilterParserError = filterExpression->parserErrorString();
      return 0;
    }

    //filter good to go
    req.setFilterExpression( mFeatureFilter );
  }
  mFilterParserError = QString();

  QgsFeatureIterator fit = mCoverageLayer->getFeatures( req );

  QScopedPointer<QgsExpression> nameExpression;
  if ( !mPageNameExpression.isEmpty() )
  {
    nameExpression.reset( new QgsExpression( mPageNameExpression ) );
    if ( nameExpression->hasParserError() )
    {
      nameExpression.reset( nullptr );
    }
    nameExpression->prepare( &expressionContext );
  }

  // We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process
  // We thus store the feature ids for future extraction
  QgsFeature feat;
  mFeatureIds.clear();
  mFeatureKeys.clear();
  int sortIdx = mCoverageLayer->fieldNameIndex( mSortKeyAttributeName );

  while ( fit.nextFeature( feat ) )
  {
    expressionContext.setFeature( feat );

    QString pageName;
    if ( !nameExpression.isNull() )
    {
      QVariant result = nameExpression->evaluate( &expressionContext );
      if ( nameExpression->hasEvalError() )
      {
        QgsMessageLog::logMessage( tr( "Atlas name eval error: %1" ).arg( nameExpression->evalErrorString() ), tr( "Composer" ) );
      }
      pageName = result.toString();
    }

    mFeatureIds.push_back( qMakePair( feat.id(), pageName ) );

    if ( mSortFeatures && sortIdx != -1 )
    {
      mFeatureKeys.insert( feat.id(), feat.attributes().at( sortIdx ) );
    }
  }

  // sort features, if asked for
  if ( !mFeatureKeys.isEmpty() )
  {
    FieldSorter sorter( mFeatureKeys, mSortAscending );
    qSort( mFeatureIds.begin(), mFeatureIds.end(), sorter );
  }

  emit numberFeaturesChanged( mFeatureIds.size() );

  //jump to first feature if currently using an atlas preview
  //need to do this in case filtering/layer change has altered matching features
  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
  {
    firstFeature();
  }

  return mFeatureIds.size();
}
Exemplo n.º 15
0
int QgsZonalStatistics::calculateStatistics( QProgressDialog *p )
{
  if ( !mPolygonLayer || mPolygonLayer->geometryType() != QgsWkbTypes::PolygonGeometry )
  {
    return 1;
  }

  QgsVectorDataProvider *vectorProvider = mPolygonLayer->dataProvider();
  if ( !vectorProvider )
  {
    return 2;
  }

  if ( !mRasterLayer )
  {
    return 3;
  }

  if ( mRasterLayer->bandCount() < mRasterBand )
  {
    return 4;
  }

  mRasterProvider = mRasterLayer->dataProvider();
  mInputNodataValue = mRasterProvider->sourceNoDataValue( mRasterBand );

  //get geometry info about raster layer
  int nCellsXProvider = mRasterProvider->xSize();
  int nCellsYProvider = mRasterProvider->ySize();
  double cellsizeX = mRasterLayer->rasterUnitsPerPixelX();
  if ( cellsizeX < 0 )
  {
    cellsizeX = -cellsizeX;
  }
  double cellsizeY = mRasterLayer->rasterUnitsPerPixelY();
  if ( cellsizeY < 0 )
  {
    cellsizeY = -cellsizeY;
  }
  QgsRectangle rasterBBox = mRasterProvider->extent();

  //add the new fields to the provider
  QList<QgsField> newFieldList;
  QString countFieldName;
  if ( mStatistics & QgsZonalStatistics::Count )
  {
    countFieldName = getUniqueFieldName( mAttributePrefix + "count", newFieldList );
    QgsField countField( countFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
    newFieldList.push_back( countField );
  }
  QString sumFieldName;
  if ( mStatistics & QgsZonalStatistics::Sum )
  {
    sumFieldName = getUniqueFieldName( mAttributePrefix + "sum", newFieldList );
    QgsField sumField( sumFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
    newFieldList.push_back( sumField );
  }
  QString meanFieldName;
  if ( mStatistics & QgsZonalStatistics::Mean )
  {
    meanFieldName = getUniqueFieldName( mAttributePrefix + "mean", newFieldList );
    QgsField meanField( meanFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
    newFieldList.push_back( meanField );
  }
  QString medianFieldName;
  if ( mStatistics & QgsZonalStatistics::Median )
  {
    medianFieldName = getUniqueFieldName( mAttributePrefix + "median", newFieldList );
    QgsField medianField( medianFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
    newFieldList.push_back( medianField );
  }
  QString stdevFieldName;
  if ( mStatistics & QgsZonalStatistics::StDev )
  {
    stdevFieldName = getUniqueFieldName( mAttributePrefix + "stdev", newFieldList );
    QgsField stdField( stdevFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
    newFieldList.push_back( stdField );
  }
  QString minFieldName;
  if ( mStatistics & QgsZonalStatistics::Min )
  {
    minFieldName = getUniqueFieldName( mAttributePrefix + "min", newFieldList );
    QgsField minField( minFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
    newFieldList.push_back( minField );
  }
  QString maxFieldName;
  if ( mStatistics & QgsZonalStatistics::Max )
  {
    maxFieldName = getUniqueFieldName( mAttributePrefix + "max", newFieldList );
    QgsField maxField( maxFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
    newFieldList.push_back( maxField );
  }
  QString rangeFieldName;
  if ( mStatistics & QgsZonalStatistics::Range )
  {
    rangeFieldName = getUniqueFieldName( mAttributePrefix + "range", newFieldList );
    QgsField rangeField( rangeFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
    newFieldList.push_back( rangeField );
  }
  QString minorityFieldName;
  if ( mStatistics & QgsZonalStatistics::Minority )
  {
    minorityFieldName = getUniqueFieldName( mAttributePrefix + "minority", newFieldList );
    QgsField minorityField( minorityFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
    newFieldList.push_back( minorityField );
  }
  QString majorityFieldName;
  if ( mStatistics & QgsZonalStatistics::Majority )
  {
    majorityFieldName = getUniqueFieldName( mAttributePrefix + "majority", newFieldList );
    QgsField majField( majorityFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
    newFieldList.push_back( majField );
  }
  QString varietyFieldName;
  if ( mStatistics & QgsZonalStatistics::Variety )
  {
    varietyFieldName = getUniqueFieldName( mAttributePrefix + "variety", newFieldList );
    QgsField varietyField( varietyFieldName, QVariant::Int, QStringLiteral( "int" ) );
    newFieldList.push_back( varietyField );
  }
  vectorProvider->addAttributes( newFieldList );

  //index of the new fields
  int countIndex = mStatistics & QgsZonalStatistics::Count ? vectorProvider->fieldNameIndex( countFieldName ) : -1;
  int sumIndex = mStatistics & QgsZonalStatistics::Sum ? vectorProvider->fieldNameIndex( sumFieldName ) : -1;
  int meanIndex = mStatistics & QgsZonalStatistics::Mean ? vectorProvider->fieldNameIndex( meanFieldName ) : -1;
  int medianIndex = mStatistics & QgsZonalStatistics::Median ? vectorProvider->fieldNameIndex( medianFieldName ) : -1;
  int stdevIndex = mStatistics & QgsZonalStatistics::StDev ? vectorProvider->fieldNameIndex( stdevFieldName ) : -1;
  int minIndex = mStatistics & QgsZonalStatistics::Min ? vectorProvider->fieldNameIndex( minFieldName ) : -1;
  int maxIndex = mStatistics & QgsZonalStatistics::Max ? vectorProvider->fieldNameIndex( maxFieldName ) : -1;
  int rangeIndex = mStatistics & QgsZonalStatistics::Range ? vectorProvider->fieldNameIndex( rangeFieldName ) : -1;
  int minorityIndex = mStatistics & QgsZonalStatistics::Minority ? vectorProvider->fieldNameIndex( minorityFieldName ) : -1;
  int majorityIndex = mStatistics & QgsZonalStatistics::Majority ? vectorProvider->fieldNameIndex( majorityFieldName ) : -1;
  int varietyIndex = mStatistics & QgsZonalStatistics::Variety ? vectorProvider->fieldNameIndex( varietyFieldName ) : -1;

  if ( ( mStatistics & QgsZonalStatistics::Count && countIndex == -1 )
       || ( mStatistics & QgsZonalStatistics::Sum && sumIndex == -1 )
       || ( mStatistics & QgsZonalStatistics::Mean && meanIndex == -1 )
       || ( mStatistics & QgsZonalStatistics::Median && medianIndex == -1 )
       || ( mStatistics & QgsZonalStatistics::StDev && stdevIndex == -1 )
       || ( mStatistics & QgsZonalStatistics::Min && minIndex == -1 )
       || ( mStatistics & QgsZonalStatistics::Max && maxIndex == -1 )
       || ( mStatistics & QgsZonalStatistics::Range && rangeIndex == -1 )
       || ( mStatistics & QgsZonalStatistics::Minority && minorityIndex == -1 )
       || ( mStatistics & QgsZonalStatistics::Majority && majorityIndex == -1 )
       || ( mStatistics & QgsZonalStatistics::Variety && varietyIndex == -1 )
     )
  {
    //failed to create a required field
    return 8;
  }

  //progress dialog
  long featureCount = vectorProvider->featureCount();
  if ( p )
  {
    p->setMaximum( featureCount );
  }

  //iterate over each polygon
  QgsFeatureRequest request;
  request.setSubsetOfAttributes( QgsAttributeList() );
  QgsFeatureIterator fi = vectorProvider->getFeatures( request );
  QgsFeature f;

  bool statsStoreValues = ( mStatistics & QgsZonalStatistics::Median ) ||
                          ( mStatistics & QgsZonalStatistics::StDev );
  bool statsStoreValueCount = ( mStatistics & QgsZonalStatistics::Minority ) ||
                              ( mStatistics & QgsZonalStatistics::Majority );

  FeatureStats featureStats( statsStoreValues, statsStoreValueCount );
  int featureCounter = 0;

  QgsChangedAttributesMap changeMap;
  while ( fi.nextFeature( f ) )
  {
    if ( p )
    {
      p->setValue( featureCounter );
    }

    if ( p && p->wasCanceled() )
    {
      break;
    }

    if ( !f.hasGeometry() )
    {
      ++featureCounter;
      continue;
    }
    QgsGeometry featureGeometry = f.geometry();

    QgsRectangle featureRect = featureGeometry.boundingBox().intersect( &rasterBBox );
    if ( featureRect.isEmpty() )
    {
      ++featureCounter;
      continue;
    }

    int offsetX, offsetY, nCellsX, nCellsY;
    if ( cellInfoForBBox( rasterBBox, featureRect, cellsizeX, cellsizeY, offsetX, offsetY, nCellsX, nCellsY ) != 0 )
    {
      ++featureCounter;
      continue;
    }

    //avoid access to cells outside of the raster (may occur because of rounding)
    if ( ( offsetX + nCellsX ) > nCellsXProvider )
    {
      nCellsX = nCellsXProvider - offsetX;
    }
    if ( ( offsetY + nCellsY ) > nCellsYProvider )
    {
      nCellsY = nCellsYProvider - offsetY;
    }

    statisticsFromMiddlePointTest( featureGeometry, offsetX, offsetY, nCellsX, nCellsY, cellsizeX, cellsizeY,
                                   rasterBBox, featureStats );

    if ( featureStats.count <= 1 )
    {
      //the cell resolution is probably larger than the polygon area. We switch to precise pixel - polygon intersection in this case
      statisticsFromPreciseIntersection( featureGeometry, offsetX, offsetY, nCellsX, nCellsY, cellsizeX, cellsizeY,
                                         rasterBBox, featureStats );
    }

    //write the statistics value to the vector data provider
    QgsAttributeMap changeAttributeMap;
    if ( mStatistics & QgsZonalStatistics::Count )
      changeAttributeMap.insert( countIndex, QVariant( featureStats.count ) );
    if ( mStatistics & QgsZonalStatistics::Sum )
      changeAttributeMap.insert( sumIndex, QVariant( featureStats.sum ) );
    if ( featureStats.count > 0 )
    {
      double mean = featureStats.sum / featureStats.count;
      if ( mStatistics & QgsZonalStatistics::Mean )
        changeAttributeMap.insert( meanIndex, QVariant( mean ) );
      if ( mStatistics & QgsZonalStatistics::Median )
      {
        std::sort( featureStats.values.begin(), featureStats.values.end() );
        int size =  featureStats.values.count();
        bool even = ( size % 2 ) < 1;
        double medianValue;
        if ( even )
        {
          medianValue = ( featureStats.values.at( size / 2 - 1 ) + featureStats.values.at( size / 2 ) ) / 2;
        }
        else //odd
        {
          medianValue = featureStats.values.at( ( size + 1 ) / 2 - 1 );
        }
        changeAttributeMap.insert( medianIndex, QVariant( medianValue ) );
      }
      if ( mStatistics & QgsZonalStatistics::StDev )
      {
        double sumSquared = 0;
        for ( int i = 0; i < featureStats.values.count(); ++i )
        {
          double diff = featureStats.values.at( i ) - mean;
          sumSquared += diff * diff;
        }
        double stdev = qPow( sumSquared / featureStats.values.count(), 0.5 );
        changeAttributeMap.insert( stdevIndex, QVariant( stdev ) );
      }
      if ( mStatistics & QgsZonalStatistics::Min )
        changeAttributeMap.insert( minIndex, QVariant( featureStats.min ) );
      if ( mStatistics & QgsZonalStatistics::Max )
        changeAttributeMap.insert( maxIndex, QVariant( featureStats.max ) );
      if ( mStatistics & QgsZonalStatistics::Range )
        changeAttributeMap.insert( rangeIndex, QVariant( featureStats.max - featureStats.min ) );
      if ( mStatistics & QgsZonalStatistics::Minority || mStatistics & QgsZonalStatistics::Majority )
      {
        QList<int> vals = featureStats.valueCount.values();
        std::sort( vals.begin(), vals.end() );
        if ( mStatistics & QgsZonalStatistics::Minority )
        {
          float minorityKey = featureStats.valueCount.key( vals.first() );
          changeAttributeMap.insert( minorityIndex, QVariant( minorityKey ) );
        }
        if ( mStatistics & QgsZonalStatistics::Majority )
        {
          float majKey = featureStats.valueCount.key( vals.last() );
          changeAttributeMap.insert( majorityIndex, QVariant( majKey ) );
        }
      }
      if ( mStatistics & QgsZonalStatistics::Variety )
        changeAttributeMap.insert( varietyIndex, QVariant( featureStats.valueCount.count() ) );
    }

    changeMap.insert( f.id(), changeAttributeMap );
    ++featureCounter;
  }

  vectorProvider->changeAttributeValues( changeMap );

  if ( p )
  {
    p->setValue( featureCount );
  }

  mPolygonLayer->updateFields();

  if ( p && p->wasCanceled() )
  {
    return 9;
  }

  return 0;
}
QVariantMap QgsLineIntersectionAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
  std::unique_ptr< QgsFeatureSource > sourceA( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
  if ( !sourceA )
    throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );

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

  const QStringList fieldsA = parameterAsFields( parameters, QStringLiteral( "INPUT_FIELDS" ), context );
  const QStringList fieldsB = parameterAsFields( parameters, QStringLiteral( "INTERSECT_FIELDS" ), context );

  QgsFields outFieldsA;
  QgsAttributeList fieldsAIndices;

  if ( fieldsA.empty() )
  {
    outFieldsA = sourceA->fields();
    for ( int i = 0; i < outFieldsA.count(); ++i )
    {
      fieldsAIndices << i;
    }
  }
  else
  {
    for ( const QString &field : fieldsA )
    {
      int index = sourceA->fields().lookupField( field );
      if ( index >= 0 )
      {
        fieldsAIndices << index;
        outFieldsA.append( sourceA->fields().at( index ) );
      }
    }
  }

  QgsFields outFieldsB;
  QgsAttributeList fieldsBIndices;

  if ( fieldsB.empty() )
  {
    outFieldsB = sourceB->fields();
    for ( int i = 0; i < outFieldsB.count(); ++i )
    {
      fieldsBIndices << i;
    }
  }
  else
  {
    for ( const QString &field : fieldsB )
    {
      int index = sourceB->fields().lookupField( field );
      if ( index >= 0 )
      {
        fieldsBIndices << index;
        outFieldsB.append( sourceB->fields().at( index ) );
      }
    }
  }

  QgsFields outFields = QgsProcessingUtils::combineFields( outFieldsA, outFieldsB );

  QString dest;
  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outFields, QgsWkbTypes::Point,  sourceA->sourceCrs() ) );
  if ( !sink )
    throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );

  QgsSpatialIndex spatialIndex( sourceB->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ).setDestinationCrs( sourceA->sourceCrs(), context.transformContext() ) ), feedback );
  QgsFeature outFeature;
  QgsFeatureIterator features = sourceA->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( fieldsAIndices ) );
  double step = sourceA->featureCount() > 0 ? 100.0 / sourceA->featureCount() : 1;
  int i = 0;
  QgsFeature inFeatureA;
  while ( features.nextFeature( inFeatureA ) )
  {
    i++;
    if ( feedback->isCanceled() )
    {
      break;
    }

    if ( !inFeatureA.hasGeometry() )
      continue;

    QgsGeometry inGeom = inFeatureA.geometry();
    QgsFeatureIds lines = spatialIndex.intersects( inGeom.boundingBox() ).toSet();
    if ( !lines.empty() )
    {
      // use prepared geometries for faster intersection tests
      std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( inGeom.constGet() ) );
      engine->prepareGeometry();

      QgsFeatureRequest request = QgsFeatureRequest().setFilterFids( lines );
      request.setDestinationCrs( sourceA->sourceCrs(), context.transformContext() );
      request.setSubsetOfAttributes( fieldsBIndices );

      QgsFeature inFeatureB;
      QgsFeatureIterator featuresB = sourceB->getFeatures( request );
      while ( featuresB.nextFeature( inFeatureB ) )
      {
        if ( feedback->isCanceled() )
        {
          break;
        }

        QgsGeometry tmpGeom = inFeatureB.geometry();
        if ( engine->intersects( tmpGeom.constGet() ) )
        {
          QgsMultiPointXY points;
          QgsGeometry intersectGeom = inGeom.intersection( tmpGeom );
          QgsAttributes outAttributes;
          for ( int a : qgis::as_const( fieldsAIndices ) )
          {
            outAttributes.append( inFeatureA.attribute( a ) );
          }
          for ( int b : qgis::as_const( fieldsBIndices ) )
          {
            outAttributes.append( inFeatureB.attribute( b ) );
          }
          if ( QgsWkbTypes::flatType( intersectGeom.wkbType() ) == QgsWkbTypes::GeometryCollection )
          {
            const QVector<QgsGeometry> geomCollection = intersectGeom.asGeometryCollection();
            for ( const QgsGeometry &part : geomCollection )
            {
              if ( part.type() == QgsWkbTypes::PointGeometry )
              {
                if ( part.isMultipart() )
                {
                  points = part.asMultiPoint();
                }
                else
                {
                  points.append( part.asPoint() );
                }
              }
            }
          }
          else if ( intersectGeom.type() == QgsWkbTypes::PointGeometry )
          {
            if ( intersectGeom.isMultipart() )
            {
              points = intersectGeom.asMultiPoint();
            }
            else
            {
              points.append( intersectGeom.asPoint() );
            }
          }
          for ( const QgsPointXY &j : qgis::as_const( points ) )
          {
            outFeature.setGeometry( QgsGeometry::fromPointXY( j ) );
            outFeature.setAttributes( outAttributes );
            sink->addFeature( outFeature, QgsFeatureSink::FastInsert );
          }
        }
      }
    }

    feedback->setProgress( i * step );

  }

  QVariantMap outputs;
  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
  return outputs;
}
int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPoint>& splitLine, bool topologicalEditing )
{
  if ( !L->hasGeometryType() )
    return 4;

  QgsFeatureList newFeatures; //store all the newly created features
  double xMin, yMin, xMax, yMax;
  QgsRectangle bBox; //bounding box of the split line
  int returnCode = 0;
  int splitFunctionReturn; //return code of QgsGeometry::splitGeometry
  int numberOfSplittedFeatures = 0;

  QgsFeatureList featureList;
  const QgsFeatureIds selectedIds = L->selectedFeaturesIds();

  if ( selectedIds.size() > 0 ) //consider only the selected features if there is a selection
  {
    featureList = L->selectedFeatures();
  }
  else //else consider all the feature that intersect the bounding box of the split line
  {
    if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 )
    {
      bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin );
      bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax );
    }
    else
    {
      return 1;
    }

    if ( bBox.isEmpty() )
    {
      //if the bbox is a line, try to make a square out of it
      if ( bBox.width() == 0.0 && bBox.height() > 0 )
      {
        bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
        bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
      }
      else if ( bBox.height() == 0.0 && bBox.width() > 0 )
      {
        bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
        bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
      }
      else
      {
        return 2;
      }
    }

    QgsFeatureIterator fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );

    QgsFeature f;
    while ( fit.nextFeature( f ) )
      featureList << QgsFeature( f );
  }

  QgsFeatureList::iterator select_it = featureList.begin();
  for ( ; select_it != featureList.end(); ++select_it )
  {
    QList<QgsGeometry*> newGeometries;
    QList<QgsPoint> topologyTestPoints;
    QgsGeometry* newGeometry = 0;
    splitFunctionReturn = select_it->geometry()->splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
    if ( splitFunctionReturn == 0 )
    {
      //change this geometry
      L->editBuffer()->changeGeometry( select_it->id(), select_it->geometry() );

      //insert new features
      for ( int i = 0; i < newGeometries.size(); ++i )
      {
        newGeometry = newGeometries.at( i );
        QgsFeature newFeature;
        newFeature.setGeometry( newGeometry );

        //use default value where possible (primary key issue), otherwise the value from the original (split) feature
        QgsAttributes newAttributes = select_it->attributes();
        QVariant defaultValue;
        for ( int j = 0; j < newAttributes.count(); ++j )
        {
          defaultValue = L->dataProvider()->defaultValue( j );
          if ( !defaultValue.isNull() )
          {
            newAttributes[ j ] = defaultValue;
          }
        }

        newFeature.setAttributes( newAttributes );

        newFeatures.append( newFeature );
      }

      if ( topologicalEditing )
      {
        QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin();
        for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
        {
          addTopologicalPoints( *topol_it );
        }
      }
      ++numberOfSplittedFeatures;
    }
    else if ( splitFunctionReturn > 1 ) //1 means no split but also no error
    {
      returnCode = splitFunctionReturn;
    }
  }

  if ( numberOfSplittedFeatures == 0 && selectedIds.size() > 0 )
  {
    //There is a selection but no feature has been split.
    //Maybe user forgot that only the selected features are split
    returnCode = 4;
  }


  //now add the new features to this vectorlayer
  L->editBuffer()->addFeatures( newFeatures );

  return returnCode;
}
Exemplo n.º 18
0
void QgsOfflineEditing::copyVectorLayer( QgsVectorLayer* layer, sqlite3* db, const QString& offlineDbPath )
{
  if ( layer == NULL )
  {
    return;
  }

  QString tableName = layer->name();

  // create table
  QString sql = QString( "CREATE TABLE '%1' (" ).arg( tableName );
  QString delim = "";
  const QgsFields& fields = layer->dataProvider()->fields();
  for ( int idx = 0; idx < fields.count(); ++idx )
  {
    QString dataType = "";
    QVariant::Type type = fields[idx].type();
    if ( type == QVariant::Int )
    {
      dataType = "INTEGER";
    }
    else if ( type == QVariant::Double )
    {
      dataType = "REAL";
    }
    else if ( type == QVariant::String )
    {
      dataType = "TEXT";
    }
    else
    {
      showWarning( tr( "Unknown data type %1" ).arg( type ) );
    }

    sql += delim + QString( "'%1' %2" ).arg( fields[idx].name() ).arg( dataType );
    delim = ",";
  }
  sql += ")";

  // add geometry column
  QString geomType = "";
  switch ( layer->wkbType() )
  {
    case QGis::WKBPoint:
      geomType = "POINT";
      break;
    case QGis::WKBMultiPoint:
      geomType = "MULTIPOINT";
      break;
    case QGis::WKBLineString:
      geomType = "LINESTRING";
      break;
    case QGis::WKBMultiLineString:
      geomType = "MULTILINESTRING";
      break;
    case QGis::WKBPolygon:
      geomType = "POLYGON";
      break;
    case QGis::WKBMultiPolygon:
      geomType = "MULTIPOLYGON";
      break;
    default:
      showWarning( tr( "QGIS wkbType %1 not supported" ).arg( layer->wkbType() ) );
      break;
  };
  QString sqlAddGeom = QString( "SELECT AddGeometryColumn('%1', 'Geometry', %2, '%3', 2)" )
                       .arg( tableName )
                       .arg( layer->crs().authid().startsWith( "EPSG:", Qt::CaseInsensitive ) ? layer->crs().authid().mid( 5 ).toLong() : 0 )
                       .arg( geomType );

  // create spatial index
  QString sqlCreateIndex = QString( "SELECT CreateSpatialIndex('%1', 'Geometry')" ).arg( tableName );

  int rc = sqlExec( db, sql );
  if ( rc == SQLITE_OK )
  {
    rc = sqlExec( db, sqlAddGeom );
    if ( rc == SQLITE_OK )
    {
      rc = sqlExec( db, sqlCreateIndex );
    }
  }

  if ( rc == SQLITE_OK )
  {
    // add new layer
    QgsVectorLayer* newLayer = new QgsVectorLayer( QString( "dbname='%1' table='%2'(Geometry) sql=" )
        .arg( offlineDbPath ).arg( tableName ), tableName + " (offline)", "spatialite" );
    if ( newLayer->isValid() )
    {
      // mark as offline layer
      newLayer->setCustomProperty( CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE, true );

      // store original layer source
      newLayer->setCustomProperty( CUSTOM_PROPERTY_REMOTE_SOURCE, layer->source() );
      newLayer->setCustomProperty( CUSTOM_PROPERTY_REMOTE_PROVIDER, layer->providerType() );

      // copy style
      bool hasLabels = layer->hasLabelsEnabled();
      if ( !hasLabels )
      {
        // NOTE: copy symbology before adding the layer so it is displayed correctly
        copySymbology( layer, newLayer );
      }

      // register this layer with the central layers registry
      QgsMapLayerRegistry::instance()->addMapLayers(
        QList<QgsMapLayer *>() << newLayer );

      if ( hasLabels )
      {
        // NOTE: copy symbology of layers with labels enabled after adding to project, as it will crash otherwise (WORKAROUND)
        copySymbology( layer, newLayer );
      }

      // TODO: layer order

      // copy features
      newLayer->startEditing();
      QgsFeature f;

      // NOTE: force feature recount for PostGIS layer, else only visible features are counted, before iterating over all features (WORKAROUND)
      layer->setSubsetString( "" );

      QgsFeatureIterator fit = layer->getFeatures();

      emit progressModeSet( QgsOfflineEditing::CopyFeatures, layer->featureCount() );
      int featureCount = 1;

      QList<QgsFeatureId> remoteFeatureIds;
      while ( fit.nextFeature( f ) )
      {
        remoteFeatureIds << f.id();

        // NOTE: Spatialite provider ignores position of geometry column
        // fill gap in QgsAttributeMap if geometry column is not last (WORKAROUND)
        int column = 0;
        QgsAttributes attrs = f.attributes();
        QgsAttributes newAttrs( attrs.count() );
        for ( int it = 0; it < attrs.count(); ++it )
        {
          newAttrs[column++] = attrs[it];
        }
        f.setAttributes( newAttrs );

        newLayer->addFeature( f, false );

        emit progressUpdated( featureCount++ );
      }
      if ( newLayer->commitChanges() )
      {
        emit progressModeSet( QgsOfflineEditing::ProcessFeatures, layer->featureCount() );
        featureCount = 1;

        // update feature id lookup
        int layerId = getOrCreateLayerId( db, newLayer->id() );
        QList<QgsFeatureId> offlineFeatureIds;

        QgsFeatureIterator fit = newLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( QgsAttributeList() ) );
        while ( fit.nextFeature( f ) )
        {
          offlineFeatureIds << f.id();
        }

        // NOTE: insert fids in this loop, as the db is locked during newLayer->nextFeature()
        sqlExec( db, "BEGIN" );
        int remoteCount = remoteFeatureIds.size();
        for ( int i = 0; i < remoteCount; i++ )
        {
          addFidLookup( db, layerId, offlineFeatureIds.at( i ), remoteFeatureIds.at( remoteCount - ( i + 1 ) ) );
          emit progressUpdated( featureCount++ );
        }
        sqlExec( db, "COMMIT" );
      }
      else
      {
        showWarning( newLayer->commitErrors().join( "\n" ) );
      }

      // remove remote layer
      QgsMapLayerRegistry::instance()->removeMapLayers(
        QStringList() << layer->id() );
    }
  }
}
Exemplo n.º 19
0
void QgsRelationReferenceWidget::init()
{
  if ( !mReadOnlySelector && mReferencedLayer )
  {
    QApplication::setOverrideCursor( Qt::WaitCursor );

    QSet<QString> requestedAttrs;

    if ( !mFilterFields.isEmpty() )
    {
      for ( const QString &fieldName : qgis::as_const( mFilterFields ) )
      {
        int idx = mReferencedLayer->fields().lookupField( fieldName );

        if ( idx == -1 )
          continue;

        QComboBox *cb = new QComboBox();
        cb->setProperty( "Field", fieldName );
        cb->setProperty( "FieldAlias", mReferencedLayer->attributeDisplayName( idx ) );
        mFilterComboBoxes << cb;
        QVariantList uniqueValues = mReferencedLayer->uniqueValues( idx ).toList();
        cb->addItem( mReferencedLayer->attributeDisplayName( idx ) );
        QVariant nullValue = QgsApplication::nullRepresentation();
        cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->fields().at( idx ).type() ) );

        std::sort( uniqueValues.begin(), uniqueValues.end(), qgsVariantLessThan );
        const auto constUniqueValues = uniqueValues;
        for ( const QVariant &v : constUniqueValues )
        {
          cb->addItem( v.toString(), v );
        }

        connect( cb, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRelationReferenceWidget::filterChanged );

        // Request this attribute for caching
        requestedAttrs << fieldName;

        mFilterLayout->addWidget( cb );
      }

      if ( mChainFilters )
      {
        QVariant nullValue = QgsApplication::nullRepresentation();

        QgsFeature ft;
        QgsFeatureIterator fit = mReferencedLayer->getFeatures();
        while ( fit.nextFeature( ft ) )
        {
          const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
          for ( int i = 0; i < count - 1; i++ )
          {
            QVariant cv = ft.attribute( mFilterFields.at( i ) );
            QVariant nv = ft.attribute( mFilterFields.at( i + 1 ) );
            QString cf = cv.isNull() ? nullValue.toString() : cv.toString();
            QString nf = nv.isNull() ? nullValue.toString() : nv.toString();
            mFilterCache[mFilterFields[i]][cf] << nf;
          }
        }

        if ( !mFilterComboBoxes.isEmpty() )
        {
          QComboBox *cb = mFilterComboBoxes.first();
          cb->setCurrentIndex( 0 );
          disableChainedComboBoxes( cb );
        }
      }
    }
    else
    {
      mFilterContainer->hide();
    }

    mComboBox->setSourceLayer( mReferencedLayer );
    mComboBox->setDisplayExpression( mReferencedLayer->displayExpression() );
    mComboBox->setAllowNull( mAllowNull );
    mComboBox->setIdentifierField( mReferencedField );

    QVariant nullValue = QgsApplication::nullRepresentation();

    if ( mChainFilters && mFeature.isValid() )
    {
      for ( int i = 0; i < mFilterFields.size(); i++ )
      {
        QVariant v = mFeature.attribute( mFilterFields[i] );
        QString f = v.isNull() ? nullValue.toString() : v.toString();
        mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
      }
    }

    // Only connect after iterating, to have only one iterator on the referenced table at once
    connect( mComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRelationReferenceWidget::comboReferenceChanged );
    //call it for the first time
    emit mComboBox->currentIndexChanged( mComboBox->currentIndex() );

    QApplication::restoreOverrideCursor();

    mInitialized = true;
  }
}
Exemplo n.º 20
0
void QgsFieldCalculator::accept()
{
  builder->saveToRecent( QStringLiteral( "fieldcalc" ) );

  if ( !mVectorLayer )
    return;

  // Set up QgsDistanceArea each time we (re-)calculate
  QgsDistanceArea myDa;

  myDa.setSourceCrs( mVectorLayer->crs().srsid() );
  myDa.setEllipsoidalMode( true );
  myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );

  QString calcString = builder->expressionText();
  QgsExpression exp( calcString );
  exp.setGeomCalculator( &myDa );
  exp.setDistanceUnits( QgsProject::instance()->distanceUnits() );
  exp.setAreaUnits( QgsProject::instance()->areaUnits() );

  QgsExpressionContext expContext( QgsExpressionContextUtils::globalProjectLayerScopes( mVectorLayer ) );

  if ( !exp.prepare( &expContext ) )
  {
    QMessageBox::critical( nullptr, tr( "Evaluation error" ), exp.evalErrorString() );
    return;
  }

  bool updatingGeom = false;

  // Test for creating expression field based on ! mUpdateExistingGroupBox checked rather
  // than on mNewFieldGroupBox checked, as if the provider does not support adding attributes
  // then mUpdateExistingGroupBox is set to not checkable, and hence is not checked.  This
  // is a minimum fix to resolve this - better would be some GUI redesign...
  if ( ! mUpdateExistingGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() )
  {
    mVectorLayer->addExpressionField( calcString, fieldDefinition() );
  }
  else
  {
    if ( !mVectorLayer->isEditable() )
      mVectorLayer->startEditing();

    QApplication::setOverrideCursor( Qt::WaitCursor );

    mVectorLayer->beginEditCommand( QStringLiteral( "Field calculator" ) );

    //update existing field
    if ( mUpdateExistingGroupBox->isChecked() || !mNewFieldGroupBox->isEnabled() )
    {
      if ( mExistingFieldComboBox->currentData().toString() == QLatin1String( "geom" ) )
      {
        //update geometry
        mAttributeId = -1;
        updatingGeom = true;
      }
      else
      {
        QMap<QString, int>::const_iterator fieldIt = mFieldMap.constFind( mExistingFieldComboBox->currentText() );
        if ( fieldIt != mFieldMap.constEnd() )
        {
          mAttributeId = fieldIt.value();
        }
      }
    }
    else
    {
      //create new field
      const QgsField newField = fieldDefinition();

      if ( !mVectorLayer->addAttribute( newField ) )
      {
        QApplication::restoreOverrideCursor();
        QMessageBox::critical( nullptr, tr( "Provider error" ), tr( "Could not add the new field to the provider." ) );
        mVectorLayer->destroyEditCommand();
        return;
      }

      //get index of the new field
      const QgsFields& fields = mVectorLayer->fields();

      for ( int idx = 0; idx < fields.count(); ++idx )
      {
        if ( fields.at( idx ).name() == mOutputFieldNameLineEdit->text() )
        {
          mAttributeId = idx;
          break;
        }
      }

      //update expression context with new fields
      expContext.setFields( mVectorLayer->fields() );
      if ( ! exp.prepare( &expContext ) )
      {
        QApplication::restoreOverrideCursor();
        QMessageBox::critical( nullptr, tr( "Evaluation error" ), exp.evalErrorString() );
        return;
      }
    }

    if ( mAttributeId == -1 && !updatingGeom )
    {
      mVectorLayer->destroyEditCommand();
      QApplication::restoreOverrideCursor();
      return;
    }

    //go through all the features and change the new attribute
    QgsFeature feature;
    bool calculationSuccess = true;
    QString error;

    bool useGeometry = exp.needsGeometry();
    int rownum = 1;

    QgsField field = !updatingGeom ? mVectorLayer->fields().at( mAttributeId ) : QgsField();

    bool newField = !mUpdateExistingGroupBox->isChecked();
    QVariant emptyAttribute;
    if ( newField )
      emptyAttribute = QVariant( field.type() );

    QgsFeatureRequest req = QgsFeatureRequest().setFlags( useGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry );
    if ( mOnlyUpdateSelectedCheckBox->isChecked() )
    {
      req.setFilterFids( mVectorLayer->selectedFeatureIds() );
    }
    QgsFeatureIterator fit = mVectorLayer->getFeatures( req );
    while ( fit.nextFeature( feature ) )
    {
      expContext.setFeature( feature );
      expContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), rownum, true ) );

      QVariant value = exp.evaluate( &expContext );
      if ( exp.hasEvalError() )
      {
        calculationSuccess = false;
        error = exp.evalErrorString();
        break;
      }
      else if ( updatingGeom )
      {
        if ( value.canConvert< QgsGeometry >() )
        {
          QgsGeometry geom = value.value< QgsGeometry >();
          mVectorLayer->changeGeometry( feature.id(), geom );
        }
      }
      else
      {
        field.convertCompatible( value );
        mVectorLayer->changeAttributeValue( feature.id(), mAttributeId, value, newField ? emptyAttribute : feature.attributes().value( mAttributeId ) );
      }

      rownum++;
    }

    QApplication::restoreOverrideCursor();

    if ( !calculationSuccess )
    {
      QMessageBox::critical( nullptr, tr( "Error" ), tr( "An error occurred while evaluating the calculation string:\n%1" ).arg( error ) );
      mVectorLayer->destroyEditCommand();
      return;
    }

    mVectorLayer->endEditCommand();
  }
  QDialog::accept();
}
QVariantMap QgsRemoveDuplicatesByAttributeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
  std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
  if ( !source )
    throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );

  const QStringList fieldNames = parameterAsFields( parameters, QStringLiteral( "FIELDS" ), context );

  QgsAttributeList attributes;
  for ( const QString &field : fieldNames )
  {
    const int index = source->fields().lookupField( field );
    if ( index < 0 )
      feedback->reportError( QObject::tr( "Field %1 not found in INPUT layer, skipping" ).arg( field ) );
    else
      attributes.append( index );
  }
  if ( attributes.isEmpty() )
    throw QgsProcessingException( QObject::tr( "No input fields found" ) );


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

  QString dupeSinkId;
  std::unique_ptr< QgsFeatureSink > dupesSink( parameterAsSink( parameters, QStringLiteral( "DUPLICATES" ), context, dupeSinkId, source->fields(),
      source->wkbType(), source->sourceCrs() ) );

  const long count = source->featureCount();
  double step = count > 0 ? 100.0 / count : 1;
  int current = 0;

  long long keptCount = 0;
  long long discardedCount = 0;

  QSet< QVariantList > matched;

  QgsFeatureIterator it = source->getFeatures( QgsFeatureRequest(), QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
  QgsFeature f;

  QVariantList dupeKey;
  dupeKey.reserve( attributes.size() );
  for ( int i : attributes )
  {
    ( void )i;
    dupeKey.append( QVariant() );
  }

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

    int i = 0;
    for ( int attr : attributes )
      dupeKey[i++] = f.attribute( attr );

    if ( matched.contains( dupeKey ) )
    {
      // duplicate
      discardedCount++;
      if ( dupesSink )
        dupesSink->addFeature( f, QgsFeatureSink::FastInsert );
    }
    else
    {
      // not duplicate
      keptCount++;
      matched.insert( dupeKey );
      noDupeSink->addFeature( f, QgsFeatureSink::FastInsert );
    }

    feedback->setProgress( current * step );
    current++;
  }

  QVariantMap outputs;
  outputs.insert( QStringLiteral( "RETAINED_COUNT" ), keptCount );
  outputs.insert( QStringLiteral( "DUPLICATE_COUNT" ), discardedCount );
  outputs.insert( QStringLiteral( "OUTPUT" ), noDupeSinkId );
  if ( dupesSink )
    outputs.insert( QStringLiteral( "DUPLICATES" ), dupeSinkId );
  return outputs;
}
Exemplo n.º 22
0
int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPoint>& splitLine, bool topologicalEditing )
{
  if ( !L->hasGeometryType() )
    return 4;

  QgsFeatureList newFeatures; //store all the newly created features
  double xMin, yMin, xMax, yMax;
  QgsRectangle bBox; //bounding box of the split line
  int returnCode = 0;
  int splitFunctionReturn; //return code of QgsGeometry::splitGeometry
  int numberOfSplittedFeatures = 0;

  QgsFeatureIterator features;
  const QgsFeatureIds selectedIds = L->selectedFeatureIds();

  if ( !selectedIds.isEmpty() ) //consider only the selected features if there is a selection
  {
    features = L->selectedFeaturesIterator();
  }
  else //else consider all the feature that intersect the bounding box of the split line
  {
    if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 )
    {
      bBox.setXMinimum( xMin );
      bBox.setYMinimum( yMin );
      bBox.setXMaximum( xMax );
      bBox.setYMaximum( yMax );
    }
    else
    {
      return 1;
    }

    if ( bBox.isEmpty() )
    {
      //if the bbox is a line, try to make a square out of it
      if ( bBox.width() == 0.0 && bBox.height() > 0 )
      {
        bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
        bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
      }
      else if ( bBox.height() == 0.0 && bBox.width() > 0 )
      {
        bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
        bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
      }
      else
      {
        //If we have a single point, we still create a non-null box
        double bufferDistance = 0.000001;
        if ( L->crs().isGeographic() )
          bufferDistance = 0.00000001;
        bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
        bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
        bBox.setYMinimum( bBox.yMinimum() - bufferDistance );
        bBox.setYMaximum( bBox.yMaximum() + bufferDistance );
      }
    }

    features = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
  }

  QgsFeature feat;
  while ( features.nextFeature( feat ) )
  {
    if ( !feat.hasGeometry() )
    {
      continue;
    }
    QList<QgsGeometry> newGeometries;
    QList<QgsPoint> topologyTestPoints;
    QgsGeometry featureGeom = feat.geometry();
    splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
    if ( splitFunctionReturn == 0 )
    {
      //change this geometry
      L->editBuffer()->changeGeometry( feat.id(), featureGeom );

      //insert new features
      for ( int i = 0; i < newGeometries.size(); ++i )
      {
        QgsFeature f = QgsVectorLayerUtils::createFeature( L, newGeometries.at( i ), feat.attributes().toMap() );
        L->editBuffer()->addFeature( f );
      }

      if ( topologicalEditing )
      {
        QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin();
        for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
        {
          addTopologicalPoints( *topol_it );
        }
      }
      ++numberOfSplittedFeatures;
    }
    else if ( splitFunctionReturn > 1 ) //1 means no split but also no error
    {
      returnCode = splitFunctionReturn;
    }
  }

  if ( numberOfSplittedFeatures == 0 && !selectedIds.isEmpty() )
  {
    //There is a selection but no feature has been split.
    //Maybe user forgot that only the selected features are split
    returnCode = 4;
  }

  return returnCode;
}
Exemplo n.º 23
0
void QgsFieldCalculator::accept()
{
  builder->saveToRecent( "fieldcalc" );

  // Set up QgsDistanceArea each time we (re-)calculate
  QgsDistanceArea myDa;

  myDa.setSourceCrs( mVectorLayer->crs().srsid() );
  myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapSettings().hasCrsTransformEnabled() );
  myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );


  QString calcString = builder->expressionText();
  QgsExpression exp( calcString );
  exp.setGeomCalculator( myDa );

  if ( !mVectorLayer || !mVectorLayer->isEditable() )
    return;

  if ( ! exp.prepare( mVectorLayer->pendingFields() ) )
  {
    QMessageBox::critical( 0, tr( "Evaluation error" ), exp.evalErrorString() );
    return;
  }

  QApplication::setOverrideCursor( Qt::WaitCursor );

  mVectorLayer->beginEditCommand( "Field calculator" );

  //update existing field
  if ( mUpdateExistingGroupBox->isChecked() || !mNewFieldGroupBox->isEnabled() )
  {
    QMap<QString, int>::const_iterator fieldIt = mFieldMap.find( mExistingFieldComboBox->currentText() );
    if ( fieldIt != mFieldMap.end() )
    {
      mAttributeId = fieldIt.value();
    }
  }
  else
  {
    //create new field
    QgsField newField( mOutputFieldNameLineEdit->text(),
                       ( QVariant::Type ) mOutputFieldTypeComboBox->itemData( mOutputFieldTypeComboBox->currentIndex(), Qt::UserRole ).toInt(),
                       mOutputFieldTypeComboBox->itemData( mOutputFieldTypeComboBox->currentIndex(), Qt::UserRole + 1 ).toString(),
                       mOutputFieldWidthSpinBox->value(),
                       mOutputFieldPrecisionSpinBox->value() );

    if ( !mVectorLayer->addAttribute( newField ) )
    {
      QApplication::restoreOverrideCursor();
      QMessageBox::critical( 0, tr( "Provider error" ), tr( "Could not add the new field to the provider." ) );
      mVectorLayer->destroyEditCommand();
      return;
    }

    //get index of the new field
    const QgsFields& fields = mVectorLayer->pendingFields();

    for ( int idx = 0; idx < fields.count(); ++idx )
    {
      if ( fields[idx].name() == mOutputFieldNameLineEdit->text() )
      {
        mAttributeId = idx;
        break;
      }
    }

    if ( ! exp.prepare( mVectorLayer->pendingFields() ) )
    {
      QApplication::restoreOverrideCursor();
      QMessageBox::critical( 0, tr( "Evaluation error" ), exp.evalErrorString() );
      return;
    }
  }

  if ( mAttributeId == -1 )
  {
    mVectorLayer->destroyEditCommand();
    QApplication::restoreOverrideCursor();
    return;
  }

  //go through all the features and change the new attribute
  QgsFeature feature;
  bool calculationSuccess = true;
  QString error;

  bool onlySelected = mOnlyUpdateSelectedCheckBox->isChecked();
  QgsFeatureIds selectedIds = mVectorLayer->selectedFeaturesIds();

  bool useGeometry = exp.needsGeometry();
  int rownum = 1;

  bool newField = !mUpdateExistingGroupBox->isChecked();
  QVariant emptyAttribute;
  if ( newField )
    emptyAttribute = QVariant( mVectorLayer->pendingFields()[mAttributeId].type() );

  QgsFeatureIterator fit = mVectorLayer->getFeatures( QgsFeatureRequest().setFlags( useGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) );
  while ( fit.nextFeature( feature ) )
  {
    if ( onlySelected )
    {
      if ( !selectedIds.contains( feature.id() ) )
      {
        continue;
      }
    }

    exp.setCurrentRowNumber( rownum );
    QVariant value = exp.evaluate( &feature );
    if ( exp.hasEvalError() )
    {
      calculationSuccess = false;
      error = exp.evalErrorString();
      break;
    }
    else
    {
      mVectorLayer->changeAttributeValue( feature.id(), mAttributeId, value, newField ? emptyAttribute : feature.attributes().value( mAttributeId ) );
    }

    rownum++;
  }

  QApplication::restoreOverrideCursor();

  if ( !calculationSuccess )
  {
    QMessageBox::critical( 0, tr( "Error" ), tr( "An error occured while evaluating the calculation string:\n%1" ).arg( error ) );
    mVectorLayer->destroyEditCommand();
    return;
  }

  mVectorLayer->endEditCommand();
  QDialog::accept();
}
Exemplo n.º 24
0
int QgsVectorLayerEditUtils::splitParts( const QList<QgsPoint>& splitLine, bool topologicalEditing )
{
  if ( !L->hasGeometryType() )
    return 4;

  double xMin, yMin, xMax, yMax;
  QgsRectangle bBox; //bounding box of the split line
  int returnCode = 0;
  int splitFunctionReturn; //return code of QgsGeometry::splitGeometry
  int numberOfSplittedParts = 0;

  QgsFeatureIterator fit;

  if ( L->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection
  {
    fit = L->selectedFeaturesIterator();
  }
  else //else consider all the feature that intersect the bounding box of the split line
  {
    if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 )
    {
      bBox.setXMinimum( xMin );
      bBox.setYMinimum( yMin );
      bBox.setXMaximum( xMax );
      bBox.setYMaximum( yMax );
    }
    else
    {
      return 1;
    }

    if ( bBox.isEmpty() )
    {
      //if the bbox is a line, try to make a square out of it
      if ( bBox.width() == 0.0 && bBox.height() > 0 )
      {
        bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
        bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
      }
      else if ( bBox.height() == 0.0 && bBox.width() > 0 )
      {
        bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
        bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
      }
      else
      {
        //If we have a single point, we still create a non-null box
        double bufferDistance = 0.000001;
        if ( L->crs().isGeographic() )
          bufferDistance = 0.00000001;
        bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
        bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
        bBox.setYMinimum( bBox.yMinimum() - bufferDistance );
        bBox.setYMaximum( bBox.yMaximum() + bufferDistance );
      }
    }

    fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
  }

  int addPartRet = 0;

  QgsFeature feat;
  while ( fit.nextFeature( feat ) )
  {
    QList<QgsGeometry> newGeometries;
    QList<QgsPoint> topologyTestPoints;
    QgsGeometry featureGeom = feat.geometry();
    splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
    if ( splitFunctionReturn == 0 )
    {
      //add new parts
      if ( !newGeometries.isEmpty() )
        featureGeom.convertToMultiType();

      for ( int i = 0; i < newGeometries.size(); ++i )
      {
        addPartRet = featureGeom.addPart( newGeometries.at( i ) );
        if ( addPartRet )
          break;
      }

      // For test only: Exception already thrown here...
      // feat.geometry()->asWkb();

      if ( !addPartRet )
      {
        L->editBuffer()->changeGeometry( feat.id(), featureGeom );
      }
      else
      {
        // Test addPartRet
        switch ( addPartRet )
        {
          case 1:
            QgsDebugMsg( "Not a multipolygon" );
            break;

          case 2:
            QgsDebugMsg( "Not a valid geometry" );
            break;

          case 3:
            QgsDebugMsg( "New polygon ring" );
            break;
        }
      }
      L->editBuffer()->changeGeometry( feat.id(), featureGeom );

      if ( topologicalEditing )
      {
        QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin();
        for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
        {
          addTopologicalPoints( *topol_it );
        }
      }
      ++numberOfSplittedParts;
    }
    else if ( splitFunctionReturn > 1 ) //1 means no split but also no error
    {
      returnCode = splitFunctionReturn;
    }
  }

  if ( numberOfSplittedParts == 0 && L->selectedFeatureCount() > 0  && returnCode == 0 )
  {
    //There is a selection but no feature has been split.
    //Maybe user forgot that only the selected features are split
    returnCode = 4;
  }

  return returnCode;
}
Exemplo n.º 25
0
// Slot called when the menu item is triggered
// If you created more menu items / toolbar buttons in initiGui, you should
// create a separate handler for each action - this single run() method will
// not be enough
void Heatmap::run()
{
  HeatmapGui d( mQGisIface->mainWindow(), QgisGui::ModalDialogFlags, &mSessionSettings );

  if ( d.exec() == QDialog::Accepted )
  {
    // everything runs here

    // Get the required data from the dialog
    QgsRectangle myBBox = d.bbox();
    int columns = d.columns();
    int rows = d.rows();
    double cellsize = d.cellSizeX(); // or d.cellSizeY();  both have the same value
    mDecay = d.decayRatio();
    int kernelShape = d.kernelShape();

    // Start working on the input vector
    QgsVectorLayer* inputLayer = d.inputVectorLayer();

    // Getting the rasterdataset in place
    GDALAllRegister();

    GDALDataset *emptyDataset;
    GDALDriver *myDriver;

    myDriver = GetGDALDriverManager()->GetDriverByName( d.outputFormat().toUtf8() );
    if ( myDriver == NULL )
    {
      QMessageBox::information( 0, tr( "GDAL driver error" ), tr( "Cannot open the driver for the specified format" ) );
      return;
    }

    double geoTransform[6] = { myBBox.xMinimum(), cellsize, 0, myBBox.yMinimum(), 0, cellsize };
    emptyDataset = myDriver->Create( d.outputFilename().toUtf8(), columns, rows, 1, GDT_Float32, NULL );
    emptyDataset->SetGeoTransform( geoTransform );
    // Set the projection on the raster destination to match the input layer
    emptyDataset->SetProjection( inputLayer->crs().toWkt().toLocal8Bit().data() );

    GDALRasterBand *poBand;
    poBand = emptyDataset->GetRasterBand( 1 );
    poBand->SetNoDataValue( NO_DATA );

    float* line = ( float * ) CPLMalloc( sizeof( float ) * columns );
    for ( int i = 0; i < columns ; i++ )
    {
      line[i] = NO_DATA;
    }
    // Write the empty raster
    for ( int i = 0; i < rows ; i++ )
    {
      poBand->RasterIO( GF_Write, 0, i, columns, 1, line, columns, 1, GDT_Float32, 0, 0 );
    }

    CPLFree( line );
    //close the dataset
    GDALClose(( GDALDatasetH ) emptyDataset );

    // open the raster in GA_Update mode
    GDALDataset *heatmapDS;
    heatmapDS = ( GDALDataset * ) GDALOpen( TO8F( d.outputFilename() ), GA_Update );
    if ( !heatmapDS )
    {
      QMessageBox::information( 0, tr( "Raster update error" ), tr( "Could not open the created raster for updating. The heatmap was not generated." ) );
      return;
    }
    poBand = heatmapDS->GetRasterBand( 1 );

    QgsAttributeList myAttrList;
    int rField = 0;
    int wField = 0;

    // Handle different radius options
    double radius;
    double radiusToMapUnits = 1;
    int myBuffer = 0;
    if ( d.variableRadius() )
    {
      rField = d.radiusField();
      myAttrList.append( rField );
      QgsDebugMsg( QString( "Radius Field index received: %1" ).arg( rField ) );

      // If not using map units, then calculate a conversion factor to convert the radii to map units
      if ( d.radiusUnit() == HeatmapGui::Meters )
      {
        radiusToMapUnits = mapUnitsOf( 1, inputLayer->crs() );
      }
    }
    else
    {
      radius = d.radius(); // radius returned by d.radius() is already in map units
      myBuffer = bufferSize( radius, cellsize );
    }

    if ( d.weighted() )
    {
      wField = d.weightField();
      myAttrList.append( wField );
    }

    // This might have attributes or mightnot have attibutes at all
    // based on the variableRadius() and weighted()
    QgsFeatureIterator fit = inputLayer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( myAttrList ) );
    int totalFeatures = inputLayer->featureCount();
    int counter = 0;

    QProgressDialog p( tr( "Creating heatmap" ), tr( "Abort" ), 0, totalFeatures, mQGisIface->mainWindow() );
    p.setWindowModality( Qt::ApplicationModal );
    p.show();

    QgsFeature myFeature;

    while ( fit.nextFeature( myFeature ) )
    {
      counter++;
      p.setValue( counter );
      QApplication::processEvents();
      if ( p.wasCanceled() )
      {
        QMessageBox::information( 0, tr( "Heatmap generation aborted" ), tr( "QGIS will now load the partially-computed raster." ) );
        break;
      }

      QgsGeometry* myPointGeometry;
      myPointGeometry = myFeature.geometry();
      // convert the geometry to point
      QgsPoint myPoint;
      myPoint = myPointGeometry->asPoint();
      // avoiding any empty points or out of extent points
      if (( myPoint.x() < myBBox.xMinimum() ) || ( myPoint.y() < myBBox.yMinimum() )
          || ( myPoint.x() > myBBox.xMaximum() ) || ( myPoint.y() > myBBox.yMaximum() ) )
      {
        continue;
      }

      // If radius is variable then fetch it and calculate new pixel buffer size
      if ( d.variableRadius() )
      {
        radius = myFeature.attribute( rField ).toDouble() * radiusToMapUnits;
        myBuffer = bufferSize( radius, cellsize );
      }

      int blockSize = 2 * myBuffer + 1; //Block SIDE would be more appropriate
      // calculate the pixel position
      unsigned int xPosition, yPosition;
      xPosition = (( myPoint.x() - myBBox.xMinimum() ) / cellsize ) - myBuffer;
      yPosition = (( myPoint.y() - myBBox.yMinimum() ) / cellsize ) - myBuffer;

      // get the data
      float *dataBuffer = ( float * ) CPLMalloc( sizeof( float ) * blockSize * blockSize );
      poBand->RasterIO( GF_Read, xPosition, yPosition, blockSize, blockSize,
                        dataBuffer, blockSize, blockSize, GDT_Float32, 0, 0 );

      double weight = 1.0;
      if ( d.weighted() )
      {
        weight = myFeature.attribute( wField ).toDouble();
      }

      for ( int xp = 0; xp <= myBuffer; xp++ )
      {
        for ( int yp = 0; yp <= myBuffer; yp++ )
        {
          double distance = sqrt( pow( xp, 2.0 ) + pow( yp, 2.0 ) );

          // is pixel outside search bandwidth of feature?
          if ( distance > myBuffer )
          {
            continue;
          }

          double pixelValue = weight * calculateKernelValue( distance, myBuffer, kernelShape );

          // clearing anamolies along the axes
          if ( xp == 0 && yp == 0 )
          {
            pixelValue /= 4;
          }
          else if ( xp == 0 || yp == 0 )
          {
            pixelValue /= 2;
          }

          int pos[4];
          pos[0] = ( myBuffer + xp ) * blockSize + ( myBuffer + yp );
          pos[1] = ( myBuffer + xp ) * blockSize + ( myBuffer - yp );
          pos[2] = ( myBuffer - xp ) * blockSize + ( myBuffer + yp );
          pos[3] = ( myBuffer - xp ) * blockSize + ( myBuffer - yp );
          for ( int p = 0; p < 4; p++ )
          {
            if ( dataBuffer[ pos[p] ] == NO_DATA )
            {
              dataBuffer[ pos[p] ] = 0;
            }
            dataBuffer[ pos[p] ] += pixelValue;
          }
        }
      }

      poBand->RasterIO( GF_Write, xPosition, yPosition, blockSize, blockSize,
                        dataBuffer, blockSize, blockSize, GDT_Float32, 0, 0 );
      CPLFree( dataBuffer );
    }
    // Finally close the dataset
    GDALClose(( GDALDatasetH ) heatmapDS );

    // Open the file in QGIS window
    mQGisIface->addRasterLayer( d.outputFilename(), QFileInfo( d.outputFilename() ).baseName() );
  }
}
Exemplo n.º 26
0
bool QgsComposerAttributeTable::getFeatureAttributes( QList<QgsAttributeMap> &attributeMaps )
{
  if ( !mVectorLayer )
  {
    return false;
  }

  attributeMaps.clear();

  //prepare filter expression
  std::auto_ptr<QgsExpression> filterExpression;
  bool activeFilter = false;
  if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
  {
    filterExpression = std::auto_ptr<QgsExpression>( new QgsExpression( mFeatureFilter ) );
    if ( !filterExpression->hasParserError() )
    {
      activeFilter = true;
    }
  }

  QgsRectangle selectionRect;
  if ( mComposerMap && mShowOnlyVisibleFeatures )
  {
    selectionRect = *mComposerMap->currentMapExtent();
    if ( mVectorLayer && mComposition->mapSettings().hasCrsTransformEnabled() )
    {
      //transform back to layer CRS
      QgsCoordinateTransform coordTransform( mVectorLayer->crs(), mComposition->mapSettings().destinationCrs() );
      try
      {
        selectionRect = coordTransform.transformBoundingBox( selectionRect, QgsCoordinateTransform::ReverseTransform );
      }
      catch ( QgsCsException &cse )
      {
        Q_UNUSED( cse );
        return false;
      }
    }
  }

  QgsFeatureRequest req;
  if ( !selectionRect.isEmpty() )
    req.setFilterRect( selectionRect );

  req.setFlags( mShowOnlyVisibleFeatures ? QgsFeatureRequest::ExactIntersect : QgsFeatureRequest::NoFlags );

  QgsFeature f;
  int counter = 0;
  QgsFeatureIterator fit = mVectorLayer->getFeatures( req );

  while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures )
  {
    //check feature against filter
    if ( activeFilter )
    {
      QVariant result = filterExpression->evaluate( &f, mVectorLayer->pendingFields() );
      // skip this feature if the filter evaluation is false
      if ( !result.toBool() )
      {
        continue;
      }
    }

    attributeMaps.push_back( QgsAttributeMap() );

    QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
    int i = 0;
    for ( ; columnIt != mColumns.constEnd(); ++columnIt )
    {
      int idx = mVectorLayer->fieldNameIndex(( *columnIt )->attribute() );
      if ( idx != -1 )
      {
        attributeMaps.last().insert( i, f.attributes()[idx] );
      }
      else
      {
        // Lets assume it's an expression
        QgsExpression* expression = new QgsExpression(( *columnIt )->attribute() );
        expression->setCurrentRowNumber( counter + 1 );
        expression->prepare( mVectorLayer->pendingFields() );
        QVariant value = expression->evaluate( f ) ;
        attributeMaps.last().insert( i, value.toString() );
      }

      i++;
    }
    ++counter;
  }

  //sort the list, starting with the last attribute
  QgsComposerAttributeTableCompare c;
  QList< QPair<int, bool> > sortColumns = sortAttributes();
  for ( int i = sortColumns.size() - 1; i >= 0; --i )
  {
    c.setSortColumn( sortColumns.at( i ).first );
    c.setAscending( sortColumns.at( i ).second );
    qStableSort( attributeMaps.begin(), attributeMaps.end(), c );
  }

  adjustFrameToSize();
  return true;
}
void QgsAttributeTableFilterModel::generateListOfVisibleFeatures()
{
  if ( !layer() )
    return;

  bool filter = false;
  QgsRectangle rect = mCanvas->mapSettings().mapToLayerCoordinates( layer(), mCanvas->extent() );
  QgsRenderContext renderContext;
  QgsFeatureRendererV2* renderer = layer()->rendererV2();

  mFilteredFeatures.clear();

  if ( !renderer )
  {
    QgsDebugMsg( "Cannot get renderer" );
    return;
  }

  const QgsMapSettings& ms = mCanvas->mapSettings();
  if ( layer()->hasScaleBasedVisibility() &&
       ( layer()->minimumScale() > ms.scale() ||
         layer()->maximumScale() <= ms.scale() ) )
  {
    QgsDebugMsg( "Out of scale limits" );
  }
  else
  {
    if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent )
    {
      // setup scale
      // mapRenderer()->renderContext()->scale is not automaticaly updated when
      // render extent changes (because it's scale is used to identify if changed
      // since last render) -> use local context
      renderContext.setExtent( ms.visibleExtent() );
      renderContext.setMapToPixel( ms.mapToPixel() );
      renderContext.setRendererScale( ms.scale() );
    }

    filter = renderer && renderer->capabilities() & QgsFeatureRendererV2::Filter;
  }

  renderer->startRender( renderContext, layer()->pendingFields() );

  QgsFeatureRequest r( masterModel()->request() );
  if ( r.filterType() == QgsFeatureRequest::FilterRect )
  {
    r.setFilterRect( r.filterRect().intersect( &rect ) );
  }
  else
  {
    r.setFilterRect( rect );
  }
  QgsFeatureIterator features = masterModel()->layerCache()->getFeatures( r );

  QgsFeature f;

  while ( features.nextFeature( f ) )
  {
    if ( !filter || renderer->willRenderFeature( f ) )
    {
      mFilteredFeatures << f.id();
    }
#if 0
    if ( t.elapsed() > 5000 )
    {
      bool cancel = false;
      emit progress( i, cancel );
      if ( cancel )
        break;

      t.restart();
    }
#endif
  }

  features.close();

  if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent )
  {
    renderer->stopRender( renderContext );
  }
}
Exemplo n.º 28
0
void QgsMapToolMoveFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
{
  QgsVectorLayer *vlayer = currentVectorLayer();
  if ( !vlayer || !vlayer->isEditable() )
  {
    delete mRubberBand;
    mRubberBand = nullptr;
    mSnapIndicator->setMatch( QgsPointLocator::Match() );
    cadDockWidget()->clear();
    notifyNotEditableLayer();
    return;
  }

  if ( !mRubberBand )
  {
    //find first geometry under mouse cursor and store iterator to it
    QgsPointXY layerCoords = toLayerCoordinates( vlayer, e->mapPoint() );
    double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() );
    QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius,
                             layerCoords.x() + searchRadius, layerCoords.y() + searchRadius );

    if ( vlayer->selectedFeatureCount() == 0 )
    {
      QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setNoAttributes() );

      //find the closest feature
      QgsGeometry pointGeometry = QgsGeometry::fromPointXY( layerCoords );
      if ( pointGeometry.isNull() )
      {
        cadDockWidget()->clear();
        return;
      }

      double minDistance = std::numeric_limits<double>::max();

      QgsFeature cf;
      QgsFeature f;
      while ( fit.nextFeature( f ) )
      {
        if ( f.hasGeometry() )
        {
          double currentDistance = pointGeometry.distance( f.geometry() );
          if ( currentDistance < minDistance )
          {
            minDistance = currentDistance;
            cf = f;
          }
        }
      }

      if ( minDistance == std::numeric_limits<double>::max() )
      {
        cadDockWidget()->clear();
        return;
      }

      mMovedFeatures.clear();
      mMovedFeatures << cf.id(); //todo: take the closest feature, not the first one...

      mRubberBand = createRubberBand( vlayer->geometryType() );
      mRubberBand->setToGeometry( cf.geometry(), vlayer );
    }
    else
    {
      mMovedFeatures = vlayer->selectedFeatureIds();

      mRubberBand = createRubberBand( vlayer->geometryType() );
      QgsFeature feat;
      QgsFeatureIterator it = vlayer->getSelectedFeatures( QgsFeatureRequest().setNoAttributes() );

      bool allFeaturesInView = true;
      QgsRectangle viewRect = mCanvas->mapSettings().mapToLayerCoordinates( vlayer, mCanvas->extent() );

      while ( it.nextFeature( feat ) )
      {
        mRubberBand->addGeometry( feat.geometry(), vlayer );

        if ( allFeaturesInView && !viewRect.intersects( feat.geometry().boundingBox() ) )
          allFeaturesInView = false;
      }

      if ( !allFeaturesInView )
      {
        // for extra safety to make sure we are not modifying geometries by accident

        int res = QMessageBox::warning( mCanvas, tr( "Move features" ),
                                        tr( "Some of the selected features are outside of the current map view. Would you still like to continue?" ),
                                        QMessageBox::Yes | QMessageBox::No );
        if ( res != QMessageBox::Yes )
        {
          mMovedFeatures.clear();
          delete mRubberBand;
          mRubberBand = nullptr;
          mSnapIndicator->setMatch( QgsPointLocator::Match() );
          return;
        }
      }
    }

    mStartPointMapCoords = e->mapPoint();
    mRubberBand->show();
  }
  else
  {
    // copy and move mode
    if ( e->button() != Qt::LeftButton )
    {
      cadDockWidget()->clear();
      delete mRubberBand;
      mRubberBand = nullptr;
      mSnapIndicator->setMatch( QgsPointLocator::Match() );
      return;
    }

    QgsPointXY startPointLayerCoords = toLayerCoordinates( ( QgsMapLayer * )vlayer, mStartPointMapCoords );
    QgsPointXY stopPointLayerCoords = toLayerCoordinates( ( QgsMapLayer * )vlayer, e->mapPoint() );

    double dx = stopPointLayerCoords.x() - startPointLayerCoords.x();
    double dy = stopPointLayerCoords.y() - startPointLayerCoords.y();

    vlayer->beginEditCommand( mMode == Move ? tr( "Feature moved" ) : tr( "Feature copied and moved" ) );

    switch ( mMode )
    {
      case Move:
        Q_FOREACH ( QgsFeatureId id, mMovedFeatures )
        {
          vlayer->translateFeature( id, dx, dy );
        }
        delete mRubberBand;
        mRubberBand = nullptr;
        mSnapIndicator->setMatch( QgsPointLocator::Match() );
        cadDockWidget()->clear();
        break;

      case CopyMove:
        QgsFeatureRequest request;
        request.setFilterFids( mMovedFeatures );
        QString *errorMsg = new QString();
        if ( !QgisApp::instance()->vectorLayerTools()->copyMoveFeatures( vlayer, request, dx, dy, errorMsg ) )
        {
          emit messageEmitted( *errorMsg, Qgis::Critical );
          delete mRubberBand;
          mRubberBand = nullptr;
          mSnapIndicator->setMatch( QgsPointLocator::Match() );
        }
        break;
    }

    vlayer->endEditCommand();
    vlayer->triggerRepaint();
  }
Exemplo n.º 29
0
QgsGeometry QgsMapToolDeletePart::partUnderPoint( QPoint point, QgsFeatureId& fid, int& partNum )
{
  QgsFeature f;
  QgsGeometry geomPart;

  switch ( vlayer->geometryType() )
  {
    case QgsWkbTypes::PointGeometry:
    case QgsWkbTypes::LineGeometry:
    {
      QgsPointLocator::Match match = mCanvas->snappingUtils()->snapToCurrentLayer( point, QgsPointLocator::Vertex | QgsPointLocator::Edge );
      if ( !match.isValid() )
        return geomPart;

      int snapVertex = match.vertexIndex();
      vlayer->getFeatures( QgsFeatureRequest().setFilterFid( match.featureId() ) ).nextFeature( f );
      QgsGeometry g = f.geometry();
      if ( !g.isMultipart() )
      {
        fid = match.featureId();
        return QgsGeometry::fromPoint( match.point() );
      }
      if ( g.wkbType() == QgsWkbTypes::MultiPoint || g.wkbType() == QgsWkbTypes::MultiPoint25D )
      {
        fid = match.featureId();
        partNum = snapVertex;
        return QgsGeometry::fromPoint( match.point() );
      }
      if ( g.wkbType() == QgsWkbTypes::MultiLineString || g.wkbType() == QgsWkbTypes::MultiLineString25D )
      {
        QgsMultiPolyline mline = g.asMultiPolyline();
        for ( int part = 0; part < mline.count(); part++ )
        {
          if ( snapVertex < mline[part].count() )
          {
            fid = match.featureId();
            partNum = part;
            return QgsGeometry::fromPolyline( mline[part] );
          }
          snapVertex -= mline[part].count();
        }
      }
      break;
    }
    case QgsWkbTypes::PolygonGeometry:
    {
      QgsPoint layerCoords = toLayerCoordinates( vlayer, point );
      double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() );
      QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius,
                               layerCoords.x() + searchRadius, layerCoords.y() + searchRadius );
      QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ) );
      fit.nextFeature( f );
      QgsGeometry g = f.geometry();
      if ( g.isEmpty() )
        return geomPart;
      if ( !g.isMultipart() )
      {
        fid = f.id();
        return geomPart;
      }
      QgsMultiPolygon mpolygon = g.asMultiPolygon();
      for ( int part = 0; part < mpolygon.count(); part++ ) // go through the polygons
      {
        const QgsPolygon& polygon = mpolygon[part];
        QgsGeometry partGeo = QgsGeometry::fromPolygon( polygon );
        if ( partGeo.contains( &layerCoords ) )
        {
          fid = f.id();
          partNum = part;
          return partGeo;
        }
      }
      break;
    }
    default:
    {
      break;
    }
  }
  return geomPart;
}
Exemplo n.º 30
0
void QgsMapToolReshape::canvasReleaseEvent( QMouseEvent * e )
{
  //check if we operate on a vector layer //todo: move this to a function in parent class to avoid duplication
  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );

  if ( !vlayer )
  {
    notifyNotVectorLayer();
    return;
  }

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

  //add point to list and to rubber band
  if ( e->button() == Qt::LeftButton )
  {
    int error = addVertex( e->pos() );
    if ( error == 1 )
    {
      //current layer is not a vector layer
      return;
    }
    else if ( error == 2 )
    {
      //problem with coordinate transformation
      QMessageBox::information( 0, tr( "Coordinate transform error" ),
                                   tr( "Cannot transform the point to the layers coordinate system" ) );
      return;
    }

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

    //find out bounding box of mCaptureList
    if ( size() < 1 )
    {
      stopCapturing();
      return;
    }
    QgsPoint firstPoint = points().at( 0 );
    QgsRectangle bbox( firstPoint.x(), firstPoint.y(), firstPoint.x(), firstPoint.y() );
    for ( int i = 1; i < size(); ++i )
    {
      bbox.combineExtentWith( points().at( i ).x(), points().at( i ).y() );
    }

    //query all the features that intersect bounding box of capture line
    QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bbox ).setSubsetOfAttributes( QgsAttributeList() ) );
    QgsFeature f;
    int reshapeReturn;
    bool reshapeDone = false;

    vlayer->beginEditCommand( tr( "Reshape" ) );
    while ( fit.nextFeature( f ) )
    {
      //query geometry
      //call geometry->reshape(mCaptureList)
      //register changed geometry in vector layer
      QgsGeometry* geom = f.geometry();
      if ( geom )
      {
        reshapeReturn = geom->reshapeGeometry( points() );
        if ( reshapeReturn == 0 )
        {
          vlayer->changeGeometry( f.id(), geom );
          reshapeDone = true;
        }
      }
    }

    if ( reshapeDone )
    {
      vlayer->endEditCommand();
    }
    else
    {
      vlayer->destroyEditCommand();
    }

    stopCapturing();
  }
}