Example #1
0
void QgsMapToolOffsetCurve::applyOffset()
{
  QgsVectorLayer* layer = currentVectorLayer();
  if ( !layer )
  {
    deleteRubberBandAndGeometry();
    notifyNotVectorLayer();
    return;
  }

  // no modification
  if ( !mGeometryModified )
  {
    deleteRubberBandAndGeometry();
    layer->destroyEditCommand();
    deleteDistanceWidget();
    return;
  }

  if ( mMultiPartGeometry )
  {
    mModifiedGeometry.convertToMultiType();
  }

  layer->beginEditCommand( tr( "Offset curve" ) );

  bool editOk;
  if ( mSourceLayerId == layer->id() && !mForceCopy )
  {
    editOk = layer->changeGeometry( mModifiedFeature, &mModifiedGeometry );
  }
  else
  {
    QgsFeature f;
    f.setGeometry( mModifiedGeometry );

    //add empty values for all fields (allows inserting attribute values via the feature form in the same session)
    QgsAttributes attrs( layer->fields().count() );
    const QgsFields& fields = layer->fields();
    for ( int idx = 0; idx < fields.count(); ++idx )
    {
      attrs[idx] = QVariant();
    }
    f.setAttributes( attrs );
    editOk = layer->addFeature( f );
  }

  if ( editOk )
  {
    layer->endEditCommand();
  }
  else
  {
    layer->destroyEditCommand();
  }

  deleteRubberBandAndGeometry();
  deleteDistanceWidget();
  delete mSnapVertexMarker;
  mSnapVertexMarker = nullptr;
  mForceCopy = false;
  mCanvas->refresh();
}
Example #2
0
void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent* e )
{
  if ( !mCanvas )
  {
    return;
  }

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

  if ( e->button() == Qt::RightButton )
  {
    deleteRubberBandAndGeometry();
    deleteDistanceWidget();
    return;
  }

  if ( !mOriginalGeometry )
  {
    deleteRubberBandAndGeometry();
    mGeometryModified = false;
    mForceCopy = false;

    if ( e->button() == Qt::RightButton )
    {
      return;
    }

    QgsSnappingUtils* snapping = mCanvas->snappingUtils();

    // store previous settings
    int oldType;
    double oldSearchRadius;
    QgsTolerance::UnitType oldSearchRadiusUnit;
    QgsSnappingUtils::SnapToMapMode oldMode = snapping->snapToMapMode();
    snapping->defaultSettings( oldType, oldSearchRadius, oldSearchRadiusUnit );

    // setup new settings (temporary)
    QSettings settings;
    snapping->setSnapToMapMode( QgsSnappingUtils::SnapAllLayers );
    snapping->setDefaultSettings( QgsPointLocator::Edge,
                                  settings.value( "/qgis/digitizing/search_radius_vertex_edit", 10 ).toDouble(),
                                  ( QgsTolerance::UnitType ) settings.value( "/qgis/digitizing/search_radius_vertex_edit_unit", QgsTolerance::Pixels ).toInt() );

    QgsPointLocator::Match match = snapping->snapToMap( e->pos() );

    // restore old settings
    snapping->setSnapToMapMode( oldMode );
    snapping->setDefaultSettings( oldType, oldSearchRadius, oldSearchRadiusUnit );

    if ( match.hasEdge() && match.layer() )
    {
      mSourceLayerId = match.layer()->id();
      QgsFeature fet;
      if ( match.layer()->getFeatures( QgsFeatureRequest( match.featureId() ) ).nextFeature( fet ) )
      {
        mForceCopy = ( e->modifiers() & Qt::ControlModifier ); //no geometry modification if ctrl is pressed
        mOriginalGeometry = createOriginGeometry( match.layer(), match, fet );
        mRubberBand = createRubberBand();
        if ( mRubberBand )
        {
          mRubberBand->setToGeometry( mOriginalGeometry, layer );
        }
        mModifiedFeature = fet.id();
        createDistanceWidget();
      }
    }
    return;
  }

  applyOffset();
}
Example #3
0
/**
 * This slot is coonnected to the map canvas. When the canvas is done drawing the slot is fired to display thee highlighting symbol
 * @param thePainter - Pointer to the QPainter object
 */
void eVisGenericEventBrowserGui::renderSymbol( QPainter* thePainter )
{

  if ( !mFeatureIds.isEmpty() && mVectorLayer )
  {
    //Get a pointer to the current feature
    QgsFeature* myFeature = featureAtId( mFeatureIds.at( mCurrentFeatureIndex ) );

    if ( !myFeature )
      return;

    QgsPoint myPoint = myFeature->geometry().asPoint();
    myPoint = mCanvas->mapSettings().layerToMapCoordinates( mVectorLayer, myPoint );

    mCanvas->getCoordinateTransform()->transform( &myPoint );

    if ( mConfiguration.isDisplayCompassBearingSet() )
    {
      //Make a copy of the pointersymbol and rotate it based on the values in the attribute field
      QPixmap myTempPixmap( mPointerSymbol.height(), mPointerSymbol.height() );
      myTempPixmap.fill( QColor( 255, 255, 255, 0 ) );
      QPainter p( &myTempPixmap );
      QMatrix wm;
      wm.translate( myTempPixmap.width() / 2, myTempPixmap.height() / 2 ); // really center

      double myBearing = mCompassBearing;
      if ( mConfiguration.isManualCompassOffsetSet() )
      {
        myBearing = mCompassBearing + mConfiguration.compassOffset();
      }
      else
      {
        myBearing = mCompassBearing + mCompassOffset;
      }

      if ( myBearing < 0.0 )
      {
        while ( myBearing < 0.0 )
          myBearing = 360.0 + myBearing;
      }
      else if ( myBearing >= 360.0 )
      {
        while ( myBearing >= 360.0 )
          myBearing = myBearing - 360.0;
      }

      wm.rotate( myBearing );

      p.setWorldMatrix( wm );
      p.drawPixmap( -mPointerSymbol.width() / 2, -mPointerSymbol.height() / 2, mPointerSymbol );

      int xShift = ( int )myPoint.x() - ( myTempPixmap.width() / 2 );
      int yShift = ( int )myPoint.y() - ( myTempPixmap.height() / 2 );
      thePainter->drawPixmap( xShift, yShift, myTempPixmap );
    }
    else
    {
      int xShift = ( int )myPoint.x() - ( mHighlightSymbol.width() / 2 );
      int yShift = ( int )myPoint.y() - ( mHighlightSymbol.height() / 2 );
      thePainter->drawPixmap( xShift, yShift, mHighlightSymbol );
    }
  }
}
Example #4
0
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
  QgsFeature feature;
  if ( !mFeaturePool->get( error->featureId(), feature ) )
  {
    error->setObsolete();
    return;
  }
  QgsGeometry g = feature.geometry();
  QgsAbstractGeometry *geometry = g.geometry();
  QgsVertexId vidx = error->vidx();

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

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

  // Fix error
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else if ( method == DeleteNode )
  {
    if ( !QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) )
    {
      error->setFixFailed( tr( "Resulting geometry is degenerate" ) );
    }
    else if ( !geometry->deleteVertex( error->vidx() ) )
    {
      error->setFixFailed( tr( "Failed to delete vertex" ) );
    }
    else
    {
      changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) );
      if ( QgsGeometryUtils::sqrDistance2D( p1, p3 ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance()
           && QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) &&
           geometry->deleteVertex( error->vidx() ) ) // error->vidx points to p3 after removing p2
      {
        changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ) );
      }
      feature.setGeometry( g );
      mFeaturePool->updateFeature( feature );
      error->setFixed( method );
    }
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}
bool QgsGeometryAnalyzer::buffer( QgsVectorLayer* layer, const QString& shapefileName, double bufferDistance,
                                  bool onlySelectedFeatures, bool dissolve, int bufferDistanceField, QProgressDialog* p )
{
    if ( !layer )
    {
        return false;
    }

    QgsVectorDataProvider* dp = layer->dataProvider();
    if ( !dp )
    {
        return false;
    }

    QGis::WkbType outputType = QGis::WKBPolygon;
    if ( dissolve )
    {
        outputType = QGis::WKBMultiPolygon;
    }
    QgsCoordinateReferenceSystem crs = layer->crs();

    QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, &crs );
    QgsFeature currentFeature;
    QgsGeometry *dissolveGeometry = nullptr; //dissolve geometry (if dissolve enabled)

    //take only selection
    if ( onlySelectedFeatures )
    {
        //use QgsVectorLayer::featureAtId
        const QgsFeatureIds selection = layer->selectedFeaturesIds();
        if ( p )
        {
            p->setMaximum( selection.size() );
        }

        int processedFeatures = 0;
        QgsFeatureIds::const_iterator it = selection.constBegin();
        for ( ; it != selection.constEnd(); ++it )
        {
            if ( p )
            {
                p->setValue( processedFeatures );
            }

            if ( p && p->wasCanceled() )
            {
                break;
            }
            if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) )
            {
                continue;
            }
            bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, &dissolveGeometry, bufferDistance, bufferDistanceField );
            ++processedFeatures;
        }

        if ( p )
        {
            p->setValue( selection.size() );
        }
    }
    //take all features
    else
    {
        QgsFeatureIterator fit = layer->getFeatures();

        int featureCount = layer->featureCount();
        if ( p )
        {
            p->setMaximum( featureCount );
        }
        int processedFeatures = 0;

        while ( fit.nextFeature( currentFeature ) )
        {
            if ( p )
            {
                p->setValue( processedFeatures );
            }
            if ( p && p->wasCanceled() )
            {
                break;
            }
            bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, &dissolveGeometry, bufferDistance, bufferDistanceField );
            ++processedFeatures;
        }
        if ( p )
        {
            p->setValue( featureCount );
        }
    }

    if ( dissolve )
    {
        QgsFeature dissolveFeature;
        if ( !dissolveGeometry )
        {
            QgsDebugMsg( "no dissolved geometry - should not happen" );
            return false;
        }
        dissolveFeature.setGeometry( dissolveGeometry );
        vWriter.addFeature( dissolveFeature );
    }
    return true;
}
void QgsGeometryCheckerSetupTab::runChecks()
{
  // Get selected layer
  const 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() ? QgsProject::instance()->mapLayer<QgsVectorLayer *>( ui.comboLineLayerIntersection->currentData().toString() ) : nullptr;
  QgsVectorLayer *followBoundaryCheckLayer = ui.comboBoxFollowBoundaries->isEnabled() ? QgsProject::instance()->mapLayer<QgsVectorLayer *>( 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;
      }
      const QgsVectorLayer::LayerOptions options { QgsProject::instance()->transformContext() };
      QgsVectorLayer *newlayer = new QgsVectorLayer( outputPath, QFileInfo( outputPath ).completeBaseName(), QStringLiteral( "ogr" ), options );
      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 ) )
  {
    featurePools.insert( layer->id(), new QgsVectorDataProviderFeaturePool( layer, 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 = QgsProject::instance()->mapLayer<QgsVectorLayer *>( ui.comboLineLayerIntersection->currentData().toString() );
    Q_ASSERT( layer );
    featurePools.insert( layer->id(), new QgsVectorDataProviderFeaturePool( layer, selectedOnly ) );
  }

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

  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, featurePools );

  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() );
}
QgsGraduatedSymbolRendererV2* QgsGraduatedSymbolRendererV2::createRenderer(
  QgsVectorLayer* vlayer,
  QString attrName,
  int classes,
  Mode mode,
  QgsSymbolV2* symbol,
  QgsVectorColorRampV2* ramp )
{
  if ( classes < 1 )
    return NULL;

  int attrNum = vlayer->fieldNameIndex( attrName );

  double minimum = vlayer->minimumValue( attrNum ).toDouble();
  double maximum = vlayer->maximumValue( attrNum ).toDouble();
  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );

  QList<double> breaks;
  QList<int> labels;
  if ( mode == EqualInterval )
  {
    breaks = _calcEqualIntervalBreaks( minimum, maximum, classes );
  }
  else if ( mode == Pretty )
  {
    breaks = _calcPrettyBreaks( minimum, maximum, classes );
  }
  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
  {
    // get values from layer
    QList<double> values;
    QgsFeature f;
    QgsAttributeList lst;
    lst.append( attrNum );

    QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( lst ) );

    // create list of non-null attribute values
    while ( fit.nextFeature( f ) )
      if ( !f.attribute( attrNum ).isNull() )
        values.append( f.attribute( attrNum ).toDouble() );

    // calculate the breaks
    if ( mode == Quantile )
    {
      breaks = _calcQuantileBreaks( values, classes );
    }
    else if ( mode == Jenks )
    {
      breaks = _calcJenksBreaks( values, classes, minimum, maximum );
    }
    else if ( mode == StdDev )
    {
      breaks = _calcStdDevBreaks( values, classes, labels );
    }
  }
  else
  {
    Q_ASSERT( false );
  }

  QgsRangeList ranges;
  double lower, upper = minimum;
  QString label;

  // "breaks" list contains all values at class breaks plus maximum as last break
  int i = 0;
  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
  {
    lower = upper; // upper border from last interval
    upper = *it;
    if ( mode == StdDev )
    {
      if ( i == 0 )
      {
        label = "< " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
      }
      else if ( i == labels.count() - 1 )
      {
        label = ">= " + QString::number( labels[i-1], 'i', 0 ) + " Std Dev";
      }
      else
      {
        label = QString::number( labels[i-1], 'i', 0 ) + " Std Dev" + " - " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
      }
    }
    else
    {
      label = QString::number( lower, 'f', 4 ) + " - " + QString::number( upper, 'f', 4 );
    }

    QgsSymbolV2* newSymbol = symbol->clone();
    double colorValue = ( breaks.count() > 1 ? ( double ) i / ( breaks.count() - 1 ) : 0 );
    newSymbol->setColor( ramp->color( colorValue ) ); // color from (0 / cl-1) to (cl-1 / cl-1)

    ranges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
  }

  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
  r->setSourceSymbol( symbol->clone() );
  r->setSourceColorRamp( ramp->clone() );
  r->setMode( mode );
  return r;
}
bool QgsGeometryAnalyzer::convexHull( QgsVectorLayer* layer, const QString& shapefileName,
                                      bool onlySelectedFeatures, int uniqueIdField, QProgressDialog* p )
{
    if ( !layer )
    {
        return false;
    }
    QgsVectorDataProvider* dp = layer->dataProvider();
    if ( !dp )
    {
        return false;
    }
    bool useField = false;
    if ( uniqueIdField == -1 )
    {
        uniqueIdField = 0;
    }
    else
    {
        useField = true;
    }
    QgsFields fields;
    fields.append( QgsField( QString( "UID" ), QVariant::String ) );
    fields.append( QgsField( QString( "AREA" ), QVariant::Double ) );
    fields.append( QgsField( QString( "PERIM" ), QVariant::Double ) );

    QGis::WkbType outputType = QGis::WKBPolygon;
    QgsCoordinateReferenceSystem crs = layer->crs();

    QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, &crs );
    QgsFeature currentFeature;
    QgsGeometry* dissolveGeometry = nullptr; //dissolve geometry
    QMultiMap<QString, QgsFeatureId> map;

    if ( onlySelectedFeatures )
    {
        //use QgsVectorLayer::featureAtId
        const QgsFeatureIds selection = layer->selectedFeaturesIds();
        QgsFeatureIds::const_iterator it = selection.constBegin();
        for ( ; it != selection.constEnd(); ++it )
        {
#if 0
            if ( p )
            {
                p->setValue( processedFeatures );
            }
            if ( p && p->wasCanceled() )
            {
                // break; // it may be better to do something else here?
                return false;
            }
#endif
            if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) )
            {
                continue;
            }
            map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() );
        }
    }
    else
    {
        QgsFeatureIterator fit = layer->getFeatures();
        while ( fit.nextFeature( currentFeature ) )
        {
#if 0
            if ( p )
            {
                p->setValue( processedFeatures );
            }
            if ( p && p->wasCanceled() )
            {
                // break; // it may be better to do something else here?
                return false;
            }
#endif
            map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() );
        }
    }

    QMultiMap<QString, QgsFeatureId>::const_iterator jt = map.constBegin();
    while ( jt != map.constEnd() )
    {
        QString currentKey = jt.key();
        int processedFeatures = 0;
        //take only selection
        if ( onlySelectedFeatures )
        {
            //use QgsVectorLayer::featureAtId
            const QgsFeatureIds selection = layer->selectedFeaturesIds();
            if ( p )
            {
                p->setMaximum( selection.size() );
            }
            processedFeatures = 0;
            while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
            {
                if ( p && p->wasCanceled() )
                {
                    break;
                }
                if ( selection.contains( jt.value() ) )
                {
                    if ( p )
                    {
                        p->setValue( processedFeatures );
                    }
                    if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) )
                    {
                        continue;
                    }
                    convexFeature( currentFeature, processedFeatures, &dissolveGeometry );
                    ++processedFeatures;
                }
                ++jt;
            }
            QList<double> values;
            if ( !dissolveGeometry )
            {
                QgsDebugMsg( "no dissolved geometry - should not happen" );
                return false;
            }
            dissolveGeometry = dissolveGeometry->convexHull();
            values = simpleMeasure( dissolveGeometry );
            QgsAttributes attributes( 3 );
            attributes[0] = QVariant( currentKey );
            attributes[1] = values.at( 0 );
            attributes[2] = values.at( 1 );
            QgsFeature dissolveFeature;
            dissolveFeature.setAttributes( attributes );
            dissolveFeature.setGeometry( dissolveGeometry );
            vWriter.addFeature( dissolveFeature );
        }
        //take all features
        else
        {
            int featureCount = layer->featureCount();
            if ( p )
            {
                p->setMaximum( featureCount );
            }
            processedFeatures = 0;
            while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
            {
                if ( p )
                {
                    p->setValue( processedFeatures );
                }

                if ( p && p->wasCanceled() )
                {
                    break;
                }
                if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) )
                {
                    continue;
                }
                convexFeature( currentFeature, processedFeatures, &dissolveGeometry );
                ++processedFeatures;
                ++jt;
            }
            QList<double> values;
            // QgsGeometry* tmpGeometry = 0;
            if ( !dissolveGeometry )
            {
                QgsDebugMsg( "no dissolved geometry - should not happen" );
                return false;
            }
            dissolveGeometry = dissolveGeometry->convexHull();
            // values = simpleMeasure( tmpGeometry );
            values = simpleMeasure( dissolveGeometry );
            QgsAttributes attributes;
            attributes[0] = QVariant( currentKey );
            attributes[1] = QVariant( values[ 0 ] );
            attributes[2] = QVariant( values[ 1 ] );
            QgsFeature dissolveFeature;
            dissolveFeature.setAttributes( attributes );
            dissolveFeature.setGeometry( dissolveGeometry );
            vWriter.addFeature( dissolveFeature );
        }
    }
    return true;
}
bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature )
{
  feature.setValid( false );

  if ( mClosed )
    return false;

  if ( mFeatureQueue.empty() )
  {
    QString fetch = QString( "FETCH FORWARD %1 FROM %2" ).arg( mFeatureQueueSize ).arg( mCursorName );
    QgsDebugMsgLevel( QString( "fetching %1 features." ).arg( mFeatureQueueSize ), 4 );
    if ( mConn->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously
    {
      QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) );
    }

    QgsPostgresResult queryResult;
    for ( ;; )
    {
      queryResult = mConn->PQgetResult();
      if ( !queryResult.result() )
        break;

      if ( queryResult.PQresultStatus() != PGRES_TUPLES_OK )
      {
        QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) );
        break;
      }

      int rows = queryResult.PQntuples();
      if ( rows == 0 )
        continue;

      for ( int row = 0; row < rows; row++ )
      {
        mFeatureQueue.enqueue( QgsFeature() );
        getFeature( queryResult, row, mFeatureQueue.back() );
      } // for each row in queue
    }
  }

  if ( mFeatureQueue.empty() )
  {
    QgsDebugMsg( QString( "Finished after %1 features" ).arg( mFetched ) );
    close();

    mSource->mShared->ensureFeaturesCountedAtLeast( mFetched );

    return false;
  }

  // Now return the next feature from the queue
  if ( !mFetchGeometry )
  {
    feature.setGeometryAndOwnership( 0, 0 );
  }
  else
  {
    QgsGeometry* featureGeom = mFeatureQueue.front().geometryAndOwnership();
    feature.setGeometry( featureGeom );
  }
  feature.setFeatureId( mFeatureQueue.front().id() );
  feature.setAttributes( mFeatureQueue.front().attributes() );

  mFeatureQueue.dequeue();
  mFetched++;

  feature.setValid( true );
  feature.setFields( &mSource->mFields ); // allow name-based attribute lookups

  return true;
}
Example #10
0
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,
    QgsGeometry* selectGeometry,
    bool doContains,
    bool doDifference,
    bool singleSelect )
{
  if ( selectGeometry->type() != QGis::Polygon )
  {
    return;
  }
  QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas );
  if ( vlayer == NULL )
  {
    return;
  }

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

  if ( canvas->mapSettings().hasCrsTransformEnabled() )
  {
    try
    {
      QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs() );
      selectGeomTrans.transform( ct );
    }
    catch ( QgsCsException &cse )
    {
      Q_UNUSED( cse );
      // catch exception for 'invalid' point and leave existing selection unchanged
      QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) );
      QgisApp::instance()->messageBar()->pushMessage(
        QObject::tr( "CRS Exception" ),
        QObject::tr( "Selection extends beyond layer's coordinate system" ),
        QgsMessageBar::WARNING,
        QgisApp::instance()->messageTimeout() );
      return;
    }
  }

  QApplication::setOverrideCursor( Qt::WaitCursor );

  QgsDebugMsg( "Selection layer: " + vlayer->name() );
  QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() );
  QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) );
  QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) );

  QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() );
  QgsFeatureRendererV2* r = vlayer->rendererV2();
  if ( r )
    r->startRender( context, vlayer->fields() );

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

  QgsFeatureIterator fit = vlayer->getFeatures( request );

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

    const QgsGeometry* g = f.constGeometry();
    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() ) );

  if ( doDifference )
  {
    QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds();

    QgsFeatureIds selectedFeatures;
    QgsFeatureIds deselectedFeatures;

    QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd();
    while ( i != newSelectedFeatures.constBegin() )
    {
      --i;
      if ( layerSelectedFeatures.contains( *i ) )
      {
        deselectedFeatures.insert( *i );
      }
      else
      {
        selectedFeatures.insert( *i );
      }
    }

    vlayer->modifySelection( selectedFeatures, deselectedFeatures );
  }
  else
  {
    vlayer->setSelectedFeatures( newSelectedFeatures );
  }

  QApplication::restoreOverrideCursor();
}
bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int row, QgsFeature &feature )
{
  feature.initAttributes( mSource->mFields.count() );

  int col = 0;

  if ( mFetchGeometry )
  {
    int returnedLength = ::PQgetlength( queryResult.result(), row, col );
    if ( returnedLength > 0 )
    {
      unsigned char *featureGeom = new unsigned char[returnedLength + 1];
      memcpy( featureGeom, PQgetvalue( queryResult.result(), row, col ), returnedLength );
      memset( featureGeom + returnedLength, 0, 1 );

      // modify 2.5D WKB types to make them compliant with OGR
      unsigned int wkbType;
      memcpy( &wkbType, featureGeom + 1, sizeof( wkbType ) );
      wkbType = QgsPostgresConn::wkbTypeFromOgcWkbType( wkbType );
      memcpy( featureGeom + 1, &wkbType, sizeof( wkbType ) );

      // change wkb type of inner geometries
      if ( wkbType == QGis::WKBMultiPoint25D ||
           wkbType == QGis::WKBMultiLineString25D ||
           wkbType == QGis::WKBMultiPolygon25D )
      {
        unsigned int numGeoms;
        memcpy( &numGeoms, featureGeom + 5, sizeof( unsigned int ) );
        unsigned char *wkb = featureGeom + 9;
        for ( unsigned int i = 0; i < numGeoms; ++i )
        {
          unsigned int localType;
          memcpy( &localType, wkb + 1, sizeof( localType ) );
          localType = QgsPostgresConn::wkbTypeFromOgcWkbType( localType );
          memcpy( wkb + 1, &localType, sizeof( localType ) );

          // skip endian and type info
          wkb += sizeof( unsigned int ) + 1;

          // skip coordinates
          switch ( wkbType )
          {
            case QGis::WKBMultiPoint25D:
              wkb += sizeof( double ) * 3;
              break;
            case QGis::WKBMultiLineString25D:
            {
              unsigned int nPoints;
              memcpy( &nPoints, wkb, sizeof( int ) );
              wkb += sizeof( int ) + sizeof( double ) * 3 * nPoints;
            }
            break;
            default:
            case QGis::WKBMultiPolygon25D:
            {
              unsigned int nRings;
              memcpy( &nRings, wkb, sizeof( int ) );
              wkb += sizeof( int );
              for ( unsigned int j = 0; j < nRings; ++j )
              {
                unsigned int nPoints;
                memcpy( &nPoints, wkb, sizeof( int ) );
                wkb += sizeof( nPoints ) + sizeof( double ) * 3 * nPoints;
              }
            }
            break;
          }
        }
      }

      feature.setGeometryAndOwnership( featureGeom, returnedLength + 1 );
    }
    else
    {
      feature.setGeometryAndOwnership( 0, 0 );
    }

    col++;
  }

  QgsFeatureId fid = 0;

  bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes;
  const QgsAttributeList& fetchAttributes = mRequest.subsetOfAttributes();

  switch ( mSource->mPrimaryKeyType )
  {
    case pktOid:
    case pktTid:
    case pktInt:
      fid = mConn->getBinaryInt( queryResult, row, col++ );
      if ( mSource->mPrimaryKeyType == pktInt &&
           ( !subsetOfAttributes || fetchAttributes.contains( mSource->mPrimaryKeyAttrs[0] ) ) )
        feature.setAttribute( mSource->mPrimaryKeyAttrs[0], fid );
      break;

    case pktFidMap:
    {
      QList<QVariant> primaryKeyVals;

      foreach ( int idx, mSource->mPrimaryKeyAttrs )
      {
        const QgsField &fld = mSource->mFields[idx];

        QVariant v = QgsPostgresProvider::convertValue( fld.type(), queryResult.PQgetvalue( row, col ) );
        primaryKeyVals << v;

        if ( !subsetOfAttributes || fetchAttributes.contains( idx ) )
          feature.setAttribute( idx, v );

        col++;
      }

      fid = mSource->mShared->lookupFid( QVariant( primaryKeyVals ) );

    }
    break;

    case pktUnknown:
      Q_ASSERT( !"FAILURE: cannot get feature with unknown primary key" );
      return false;
  }

  feature.setFeatureId( fid );
  QgsDebugMsgLevel( QString( "fid=%1" ).arg( fid ), 4 );

  // iterate attributes
  if ( subsetOfAttributes )
  {
    foreach ( int idx, fetchAttributes )
      getFeatureAttribute( idx, queryResult, row, col, feature );
  }
  else
  {
    for ( int idx = 0; idx < mSource->mFields.count(); ++idx )
void QgsAttributeTableFilterModel::generateListOfVisibleFeatures()
{
  if ( !layer() )
    return;

  bool filter = false;
  QgsRectangle rect = mCanvas->mapSettings().mapToLayerCoordinates( layer(), mCanvas->extent() );
  QgsRenderContext renderContext;
  renderContext.expressionContext() << QgsExpressionContextUtils::globalScope()
  << QgsExpressionContextUtils::projectScope()
  << QgsExpressionContextUtils::layerScope( layer() );
  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()->fields() );

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

  QgsFeature f;

  while ( features.nextFeature( f ) )
  {
    renderContext.expressionContext().setFeature( f );
    if ( !filter || renderer->willRenderFeature( f, renderContext ) )
    {
      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 );
  }
}
Example #13
0
bool QgsAfsProvider::getFeature( const QgsFeatureId &id, QgsFeature &f, bool fetchGeometry, const QList<int>& /*fetchAttributes*/, const QgsRectangle filterRect )
{
  // If cached, return cached feature
  QMap<QgsFeatureId, QgsFeature>::const_iterator it = mCache.find( id );
  if ( it != mCache.end() )
  {
    f = it.value();
    return filterRect.isNull() || f.geometry()->intersects( filterRect );
  }

  // Determine attributes to fetch
  /*QStringList fetchAttribNames;
  foreach ( int idx, fetchAttributes )
    fetchAttribNames.append( mFields.at( idx ).name() );
  */

  // When fetching from server, fetch all attributes and geometry by default so that we can cache them
  QStringList fetchAttribNames;
  QList<int> fetchAttribIdx;
  for ( int idx = 0, n = mFields.size(); idx < n; ++idx )
  {
    fetchAttribNames.append( mFields.at( idx ).name() );
    fetchAttribIdx.append( idx );
  }
  fetchGeometry = true;

  // Fetch 100 features at the time
  int startId = ( id / 100 ) * 100;
  int stopId = qMin( startId + 100, mObjectIds.length() );
  QList<quint32> objectIds;
  for ( int i = startId; i < stopId; ++i )
  {
    objectIds.append( mObjectIds[i] );
  }


  // Query
  QString errorTitle, errorMessage;
  QVariantMap queryData = QgsArcGisRestUtils::getObjects(
                            mDataSource.param( "url" ), objectIds, mDataSource.param( "crs" ), fetchGeometry,
                            fetchAttribNames, QgsWKBTypes::hasM( mGeometryType ), QgsWKBTypes::hasZ( mGeometryType ),
                            filterRect, errorTitle, errorMessage );
  if ( queryData.isEmpty() )
  {
    const_cast<QgsAfsProvider*>( this )->pushError( errorTitle + ": " + errorMessage );
    QgsDebugMsg( "Query returned empty result" );
    return false;
  }

  QVariantList featuresData = queryData["features"].toList();
  if ( featuresData.isEmpty() )
  {
    QgsDebugMsg( "Query returned no features" );
    return false;
  }
  for ( int i = 0, n = featuresData.size(); i < n; ++i )
  {
    QVariantMap featureData = featuresData[i].toMap();
    QgsFeature feature;

    // Set FID
    feature.setFeatureId( startId + i );

    // Set attributes
    if ( !fetchAttribIdx.isEmpty() )
    {
      QVariantMap attributesData = featureData["attributes"].toMap();
      feature.setFields( mFields );
      QgsAttributes attributes( mFields.size() );
      foreach ( int idx, fetchAttribIdx )
      {
        attributes[idx] = attributesData[mFields.at( idx ).name()];
      }
      feature.setAttributes( attributes );
    }

    // Set geometry
    if ( fetchGeometry )
    {
      QVariantMap geometryData = featureData["geometry"].toMap();
      QgsAbstractGeometryV2* geometry = QgsArcGisRestUtils::parseEsriGeoJSON( geometryData, queryData["geometryType"].toString(),
                                        QgsWKBTypes::hasM( mGeometryType ), QgsWKBTypes::hasZ( mGeometryType ) );
      // Above might return 0, which is ok since in theory empty geometries are allowed
      feature.setGeometry( new QgsGeometry( geometry ) );
    }
    feature.setValid( true );
    mCache.insert( feature.id(), feature );
  }
Example #14
0
void QgsMapToolNodeTool::createTopologyRubberBands( QgsVectorLayer* vlayer, const QList<QgsVertexEntry*> &vertexMap, int vertex )
{
  QMultiMap<double, QgsSnappingResult> currentResultList;
  QgsGeometry *geometry = mSelectedFeature->geometry();

  // snap from current vertex
  currentResultList.clear();
  vlayer->snapWithContext( vertexMap[vertex]->point(), ZERO_TOLERANCE, currentResultList, QgsSnapper::SnapToVertex );
  QMultiMap<double, QgsSnappingResult>::iterator resultIt =  currentResultList.begin();

  for ( ; resultIt != currentResultList.end(); ++resultIt )
  {
    // move all other
    if ( mSelectedFeature->featureId() != resultIt.value().snappedAtGeometry )
    {
      if ( mTopologyMovingVertexes.contains( resultIt.value().snappedAtGeometry ) )
      {
        if ( mTopologyMovingVertexes[resultIt.value().snappedAtGeometry]->contains( resultIt.value().snappedVertexNr ) )
        {
          // skip vertex already exists in some rubberband
          continue;
        }
      }
      QgsRubberBand* trb = new QgsRubberBand( mCanvas, QGis::Line );
      mTopologyRubberBand.append( trb );
      int rbId = mTopologyRubberBand.size() - 1;
      trb->setWidth( 1 );
      trb->setColor( Qt::red );

      int tVertex = resultIt.value().snappedVertexNr;
      int tVertexBackup = -1, tVertexAfter = -1;
      int tVertexFirst = tVertex; // vertex number to check for cycling
      QgsFeature topolFeature;

      vlayer->getFeatures( QgsFeatureRequest().setFilterFid( resultIt.value().snappedAtGeometry ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( topolFeature );
      QgsGeometry* topolGeometry = topolFeature.geometry();

      while ( tVertex != -1 ) // looking for first vertex to rubberband
      {
        tVertexBackup = tVertex;
        topolGeometry->adjacentVertices( tVertex, tVertex, tVertexAfter );
        if ( tVertex == -1 || tVertex == tVertexFirst )
          break; // check if this is not first vertex of the feature or cycling error
        // if closest vertex is not from selected feature or is not selected end
        double dist;
        QgsPoint point = topolGeometry->vertexAt( tVertex );
        int at, before, after;
        geometry->closestVertex( point, at, before, after, dist );
        if ( dist > ZERO_TOLERANCE || !vertexMap[at]->isSelected() ) // problem with double precision
        {
          break; // found first vertex
        }
      }

      int movingPointIndex = 0;
      Vertexes* movingPoints = new Vertexes();
      Vertexes* addedPoints = new Vertexes();
      if ( mTopologyMovingVertexes.contains( resultIt.value().snappedAtGeometry ) )
      {
        addedPoints = mTopologyMovingVertexes[ resultIt.value().snappedAtGeometry ];
      }
      if ( tVertex == -1 ) // adding first point if needed
      {
        tVertex = tVertexBackup;
      }
      else
      {
        trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) );
        if ( tVertex == tVertexFirst ) // cycle first vertex need to be added also
        {
          movingPoints->insert( movingPointIndex );
        }
        movingPointIndex = 1;
        topolGeometry->adjacentVertices( tVertex, tVertexAfter, tVertex );
      }

      while ( tVertex != -1 )
      {
        // if closest vertex is not from selected feature or is not selected end
        double dist;
        QgsPoint point = topolGeometry->vertexAt( tVertex );
        int at, before, after;
        geometry->closestVertex( point, at, before, after, dist );
        // find first no matching vertex
        if ( dist > ZERO_TOLERANCE || !vertexMap[at]->isSelected() ) // problem with double precision
        {
          trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) );
          break; // found first vertex
        }
        else // add moving point to rubberband
        {
          if ( addedPoints->contains( tVertex ) )
            break; // just preventing to circle
          trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) );
          movingPoints->insert( movingPointIndex );
          movingPointIndex++;
          addedPoints->insert( tVertex );
        }
        topolGeometry->adjacentVertices( tVertex, tVertexAfter, tVertex );
      }
      mTopologyMovingVertexes.insert( resultIt.value().snappedAtGeometry, addedPoints );
      mTopologyRubberBandVertexes.insert( rbId, movingPoints );
    }
  }
}
Example #15
0
void QgsMapToolRotateFeature::canvasReleaseEvent( QgsMapMouseEvent *e )
{
  deleteRotationWidget();

  if ( !mCanvas )
  {
    return;
  }

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

  if ( e->button() == Qt::RightButton )
  {
    deleteRubberband();
    mRotationActive = false;
    return;
  }

  // place anchor point on CTRL + click
  if ( e->modifiers() & Qt::ControlModifier )
  {
    if ( !mAnchorPoint )
    {
      return;
    }
    mAnchorPoint->setCenter( toMapCoordinates( e->pos() ) );
    mStartPointMapCoords = toMapCoordinates( e->pos() );
    mStPoint = e->pos();
    return;
  }

  // Initialize rotation if not yet active
  if ( !mRotationActive )
  {
    mRotation = 0;
    mRotationOffset = 0;

    deleteRubberband();

    mInitialPos = e->pos();

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

    QgsPointXY 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.isNull() )
      {
        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() )
      {
        emit messageEmitted( tr( "Could not find a nearby feature in the current layer." ) );
        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->selectedFeatureIds();

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

      QgsFeature feat;
      QgsFeatureIterator it = vlayer->getSelectedFeatures();
      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 = std::atan2( YDistance, XDistance ) * ( 180 / M_PI );

    createRotationWidget();
    if ( e->modifiers() & Qt::ShiftModifier )
    {
      if ( mRotationWidget )
      {
        mRotationWidget->setMagnet( 45 );
      }
    }

    mRotationActive = true;

    return;
  }

  applyRotation( mRotation );
}
bool QgsGeometryAnalyzer::extent( QgsVectorLayer* layer,
                                  const QString& shapefileName,
                                  bool onlySelectedFeatures,
                                  QProgressDialog * )
{
    if ( !layer )
    {
        return false;
    }

    QgsVectorDataProvider* dp = layer->dataProvider();
    if ( !dp )
    {
        return false;
    }

    QGis::WkbType outputType = QGis::WKBPolygon;
    QgsCoordinateReferenceSystem crs = layer->crs();

    QgsFields fields;
    fields.append( QgsField( QString( "MINX" ), QVariant::Double ) );
    fields.append( QgsField( QString( "MINY" ), QVariant::Double ) );
    fields.append( QgsField( QString( "MAXX" ), QVariant::Double ) );
    fields.append( QgsField( QString( "MAXY" ), QVariant::Double ) );
    fields.append( QgsField( QString( "CNTX" ), QVariant::Double ) );
    fields.append( QgsField( QString( "CNTY" ), QVariant::Double ) );
    fields.append( QgsField( QString( "AREA" ), QVariant::Double ) );
    fields.append( QgsField( QString( "PERIM" ), QVariant::Double ) );
    fields.append( QgsField( QString( "HEIGHT" ), QVariant::Double ) );
    fields.append( QgsField( QString( "WIDTH" ), QVariant::Double ) );

    QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, &crs );

    QgsRectangle rect;
    if ( onlySelectedFeatures )  // take only selection
    {
        rect = layer->boundingBoxOfSelected();
    }
    else
    {
        rect = layer->extent();
    }

    double minx = rect.xMinimum();
    double miny = rect.yMinimum();
    double maxx = rect.xMaximum();
    double maxy = rect.yMaximum();
    double height = rect.height();
    double width = rect.width();
    double cntx = minx + ( width / 2.0 );
    double cnty = miny + ( height / 2.0 );
    double area = width * height;
    double perim = ( 2 * width ) + ( 2 * height );

    QgsFeature feat;
    QgsAttributes attrs( 10 );
    attrs[0] = QVariant( minx );
    attrs[1] = QVariant( miny );
    attrs[2] = QVariant( maxx );
    attrs[3] = QVariant( maxy );
    attrs[4] = QVariant( cntx );
    attrs[5] = QVariant( cnty );
    attrs[6] = QVariant( area );
    attrs[7] = QVariant( perim );
    attrs[8] = QVariant( height );
    attrs[9] = QVariant( width );
    feat.setAttributes( attrs );
    feat.setGeometry( QgsGeometry::fromRect( rect ) );
    vWriter.addFeature( feat );
    return true;
}
void QgsDelimitedTextProvider::scanFile( bool buildIndexes )
{
  QStringList messages;

  // assume the layer is invalid until proven otherwise

  mLayerValid = false;
  mValid = false;
  mRescanRequired = false;

  clearInvalidLines();

  // Initiallize indexes

  resetIndexes();
  bool buildSpatialIndex = buildIndexes && nullptr != mSpatialIndex;

  // No point building a subset index if there is no geometry, as all
  // records will be included.

  bool buildSubsetIndex = buildIndexes && mBuildSubsetIndex && mGeomRep != GeomNone;

  if ( ! mFile->isValid() )
  {
    // uri is invalid so the layer must be too...

    messages.append( tr( "File cannot be opened or delimiter parameters are not valid" ) );
    reportErrors( messages );
    QgsDebugMsg( "Delimited text source invalid - filename or delimiter parameters" );
    return;
  }

  // Open the file and get number of rows, etc. We assume that the
  // file has a header row and process accordingly. Caller should make
  // sure that the delimited file is properly formed.

  if ( mGeomRep == GeomAsWkt )
  {
    mWktFieldIndex = mFile->fieldIndex( mWktFieldName );
    if ( mWktFieldIndex < 0 )
    {
      messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "Wkt" ), mWktFieldName ) );
    }
  }
  else if ( mGeomRep == GeomAsXy )
  {
    mXFieldIndex = mFile->fieldIndex( mXFieldName );
    mYFieldIndex = mFile->fieldIndex( mYFieldName );
    if ( mXFieldIndex < 0 )
    {
      messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "X" ), mWktFieldName ) );
    }
    if ( mYFieldIndex < 0 )
    {
      messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "Y" ), mWktFieldName ) );
    }
  }
  if ( !messages.isEmpty() )
  {
    reportErrors( messages );
    QgsDebugMsg( "Delimited text source invalid - missing geometry fields" );
    return;
  }

  // Scan the entire file to determine
  // 1) the number of fields (this is handled by QgsDelimitedTextFile mFile
  // 2) the number of valid features.  Note that the selection of valid features
  //    should match the code in QgsDelimitedTextFeatureIterator
  // 3) the geometric extents of the layer
  // 4) the type of each field
  //
  // Also build subset and spatial indexes.

  QStringList parts;
  long nEmptyRecords = 0;
  long nBadFormatRecords = 0;
  long nIncompatibleGeometry = 0;
  long nInvalidGeometry = 0;
  long nEmptyGeometry = 0;
  mNumberFeatures = 0;
  mExtent = QgsRectangle();

  QList<bool> isEmpty;
  QList<bool> couldBeInt;
  QList<bool> couldBeLongLong;
  QList<bool> couldBeDouble;
  bool foundFirstGeometry = false;

  while ( true )
  {
    QgsDelimitedTextFile::Status status = mFile->nextRecord( parts );
    if ( status == QgsDelimitedTextFile::RecordEOF )
      break;
    if ( status != QgsDelimitedTextFile::RecordOk )
    {
      nBadFormatRecords++;
      recordInvalidLine( tr( "Invalid record format at line %1" ) );
      continue;
    }
    // Skip over empty records
    if ( recordIsEmpty( parts ) )
    {
      nEmptyRecords++;
      continue;
    }

    // Check geometries are valid
    bool geomValid = true;

    if ( mGeomRep == GeomAsWkt )
    {
      if ( mWktFieldIndex >= parts.size() || parts[mWktFieldIndex].isEmpty() )
      {
        nEmptyGeometry++;
        mNumberFeatures++;
      }
      else
      {
        // Get the wkt - confirm it is valid, get the type, and
        // if compatible with the rest of file, add to the extents

        QString sWkt = parts[mWktFieldIndex];
        QgsGeometry geom;
        if ( !mWktHasPrefix && sWkt.indexOf( sWktPrefixRegexp ) >= 0 )
          mWktHasPrefix = true;
        geom = geomFromWkt( sWkt, mWktHasPrefix );

        if ( !geom.isNull() )
        {
          QgsWkbTypes::Type type = geom.wkbType();
          if ( type != QgsWkbTypes::NoGeometry )
          {
            if ( mGeometryType == QgsWkbTypes::UnknownGeometry || geom.type() == mGeometryType )
            {
              mGeometryType = geom.type();
              if ( !foundFirstGeometry )
              {
                mNumberFeatures++;
                mWkbType = type;
                mExtent = geom.boundingBox();
                foundFirstGeometry = true;
              }
              else
              {
                mNumberFeatures++;
                if ( geom.isMultipart() )
                  mWkbType = type;
                QgsRectangle bbox( geom.boundingBox() );
                mExtent.combineExtentWith( bbox );
              }
              if ( buildSpatialIndex )
              {
                QgsFeature f;
                f.setId( mFile->recordId() );
                f.setGeometry( geom );
                mSpatialIndex->insertFeature( f );
              }
            }
            else
            {
              nIncompatibleGeometry++;
              geomValid = false;
            }
          }
        }
        else
        {
          geomValid = false;
          nInvalidGeometry++;
          recordInvalidLine( tr( "Invalid WKT at line %1" ) );
        }
      }
    }
    else if ( mGeomRep == GeomAsXy )
    {
      // Get the x and y values, first checking to make sure they
      // aren't null.

      QString sX = mXFieldIndex < parts.size() ? parts[mXFieldIndex] : QString();
      QString sY = mYFieldIndex < parts.size() ? parts[mYFieldIndex] : QString();
      if ( sX.isEmpty() && sY.isEmpty() )
      {
        nEmptyGeometry++;
        mNumberFeatures++;
      }
      else
      {
        QgsPointXY pt;
        bool ok = pointFromXY( sX, sY, pt, mDecimalPoint, mXyDms );

        if ( ok )
        {
          if ( foundFirstGeometry )
          {
            mExtent.combineExtentWith( pt.x(), pt.y() );
          }
          else
          {
            // Extent for the first point is just the first point
            mExtent.set( pt.x(), pt.y(), pt.x(), pt.y() );
            mWkbType = QgsWkbTypes::Point;
            mGeometryType = QgsWkbTypes::PointGeometry;
            foundFirstGeometry = true;
          }
          mNumberFeatures++;
          if ( buildSpatialIndex && std::isfinite( pt.x() ) && std::isfinite( pt.y() ) )
          {
            QgsFeature f;
            f.setId( mFile->recordId() );
            f.setGeometry( QgsGeometry::fromPointXY( pt ) );
            mSpatialIndex->insertFeature( f );
          }
        }
        else
        {
          geomValid = false;
          nInvalidGeometry++;
          recordInvalidLine( tr( "Invalid X or Y fields at line %1" ) );
        }
      }
    }
    else
    {
      mWkbType = QgsWkbTypes::NoGeometry;
      mNumberFeatures++;
    }

    if ( !geomValid )
      continue;

    if ( buildSubsetIndex )
      mSubsetIndex.append( mFile->recordId() );


    // If we are going to use this record, then assess the potential types of each column

    for ( int i = 0; i < parts.size(); i++ )
    {

      QString &value = parts[i];
      // Ignore empty fields - spreadsheet generated CSV files often
      // have random empty fields at the end of a row
      if ( value.isEmpty() )
        continue;

      // Expand the columns to include this non empty field if necessary

      while ( couldBeInt.size() <= i )
      {
        isEmpty.append( true );
        couldBeInt.append( false );
        couldBeLongLong.append( false );
        couldBeDouble.append( false );
      }

      // If this column has been empty so far then initiallize it
      // for possible types

      if ( isEmpty[i] )
      {
        isEmpty[i] = false;
        couldBeInt[i] = true;
        couldBeLongLong[i] = true;
        couldBeDouble[i] = true;
      }

      if ( ! mDetectTypes )
      {
        continue;
      }

      // Now test for still valid possible types for the field
      // Types are possible until first record which cannot be parsed

      if ( couldBeInt[i] )
      {
        value.toInt( &couldBeInt[i] );
      }

      if ( couldBeLongLong[i] && ! couldBeInt[i] )
      {
        value.toLongLong( &couldBeLongLong[i] );
      }

      if ( couldBeDouble[i] && ! couldBeLongLong[i] )
      {
        if ( ! mDecimalPoint.isEmpty() )
        {
          value.replace( mDecimalPoint, QLatin1String( "." ) );
        }
        value.toDouble( &couldBeDouble[i] );
      }
    }
  }

  // Now create the attribute fields.  Field types are integer by preference,
  // failing that double, failing that text.

  QStringList fieldNames = mFile->fieldNames();
  mFieldCount = fieldNames.size();
  attributeColumns.clear();
  attributeFields.clear();

  QString csvtMessage;
  QStringList csvtTypes = readCsvtFieldTypes( mFile->fileName(), &csvtMessage );

  for ( int i = 0; i < fieldNames.size(); i++ )
  {
    // Skip over WKT field ... don't want to display in attribute table
    if ( i == mWktFieldIndex )
      continue;

    // Add the field index lookup for the column
    attributeColumns.append( i );
    QVariant::Type fieldType = QVariant::String;
    QString typeName = QStringLiteral( "text" );
    if ( i < csvtTypes.size() )
    {
      typeName = csvtTypes[i];
    }
    else if ( mDetectTypes && i < couldBeInt.size() )
    {
      if ( couldBeInt[i] )
      {
        typeName = QStringLiteral( "integer" );
      }
      else if ( couldBeLongLong[i] )
      {
        typeName = QStringLiteral( "longlong" );
      }
      else if ( couldBeDouble[i] )
      {
        typeName = QStringLiteral( "double" );
      }
    }
    if ( typeName == QStringLiteral( "integer" ) )
    {
      fieldType = QVariant::Int;
    }
    else if ( typeName == QStringLiteral( "longlong" ) )
    {
      fieldType = QVariant::LongLong;
    }
    else if ( typeName == QStringLiteral( "real" ) || typeName == QStringLiteral( "double" ) )
    {
      typeName = QStringLiteral( "double" );
      fieldType = QVariant::Double;
    }
    else
    {
      typeName = QStringLiteral( "text" );
    }
    attributeFields.append( QgsField( fieldNames[i], fieldType, typeName ) );
  }


  QgsDebugMsg( "Field count for the delimited text file is " + QString::number( attributeFields.size() ) );
  QgsDebugMsg( "geometry type is: " + QString::number( mWkbType ) );
  QgsDebugMsg( "feature count is: " + QString::number( mNumberFeatures ) );

  QStringList warnings;
  if ( ! csvtMessage.isEmpty() )
    warnings.append( csvtMessage );
  if ( nBadFormatRecords > 0 )
    warnings.append( tr( "%1 records discarded due to invalid format" ).arg( nBadFormatRecords ) );
  if ( nEmptyGeometry > 0 )
    warnings.append( tr( "%1 records have missing geometry definitions" ).arg( nEmptyGeometry ) );
  if ( nInvalidGeometry > 0 )
    warnings.append( tr( "%1 records discarded due to invalid geometry definitions" ).arg( nInvalidGeometry ) );
  if ( nIncompatibleGeometry > 0 )
    warnings.append( tr( "%1 records discarded due to incompatible geometry types" ).arg( nIncompatibleGeometry ) );

  reportErrors( warnings );

  // Decide whether to use subset ids to index records rather than simple iteration through all
  // If more than 10% of records are being skipped, then use index.  (Not based on any experimentation,
  // could do with some analysis?)

  if ( buildSubsetIndex )
  {
    long recordCount = mFile->recordCount();
    recordCount -= recordCount / SUBSET_ID_THRESHOLD_FACTOR;
    mUseSubsetIndex = mSubsetIndex.size() < recordCount;
    if ( ! mUseSubsetIndex )
      mSubsetIndex = QList<quintptr>();
  }

  mUseSpatialIndex = buildSpatialIndex;

  mValid = mGeometryType != QgsWkbTypes::UnknownGeometry;
  mLayerValid = mValid;

  // If it is valid, then watch for changes to the file
  connect( mFile.get(), &QgsDelimitedTextFile::fileUpdated, this, &QgsDelimitedTextProvider::onFileUpdated );
}
bool QgsGeometryAnalyzer::dissolve( QgsVectorLayer* layer, const QString& shapefileName,
                                    bool onlySelectedFeatures, int uniqueIdField, QProgressDialog* p )
{
    if ( !layer )
    {
        return false;
    }
    QgsVectorDataProvider* dp = layer->dataProvider();
    if ( !dp )
    {
        return false;
    }
    bool useField = false;
    if ( uniqueIdField == -1 )
    {
        uniqueIdField = 0;
    }
    else
    {
        useField = true;
    }

    QGis::WkbType outputType = dp->geometryType();
    QgsCoordinateReferenceSystem crs = layer->crs();

    QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, &crs );
    QgsFeature currentFeature;
    QMultiMap<QString, QgsFeatureId> map;

    if ( onlySelectedFeatures )
    {
        //use QgsVectorLayer::featureAtId
        const QgsFeatureIds selection = layer->selectedFeaturesIds();
        QgsFeatureIds::const_iterator it = selection.constBegin();
        for ( ; it != selection.constEnd(); ++it )
        {
            if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) )
            {
                continue;
            }
            map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() );
        }
    }
    else
    {
        QgsFeatureIterator fit = layer->getFeatures();
        while ( fit.nextFeature( currentFeature ) )
        {
            map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() );
        }
    }

    QgsGeometry *dissolveGeometry = nullptr; //dissolve geometry
    QMultiMap<QString, QgsFeatureId>::const_iterator jt = map.constBegin();
    QgsFeature outputFeature;
    while ( jt != map.constEnd() )
    {
        QString currentKey = jt.key();
        int processedFeatures = 0;
        bool first = true;
        //take only selection
        if ( onlySelectedFeatures )
        {
            //use QgsVectorLayer::featureAtId
            const QgsFeatureIds selection = layer->selectedFeaturesIds();
            if ( p )
            {
                p->setMaximum( selection.size() );
            }
            while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
            {
                if ( p && p->wasCanceled() )
                {
                    break;
                }
                if ( selection.contains( jt.value() ) )
                {
                    if ( p )
                    {
                        p->setValue( processedFeatures );
                    }
                    if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) )
                    {
                        continue;
                    }
                    if ( first )
                    {
                        outputFeature.setAttributes( currentFeature.attributes() );
                        first = false;
                    }
                    dissolveFeature( currentFeature, processedFeatures, &dissolveGeometry );
                    ++processedFeatures;
                }
                ++jt;
            }
        }
        //take all features
        else
        {
            int featureCount = layer->featureCount();
            if ( p )
            {
                p->setMaximum( featureCount );
            }
            while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
            {
                if ( p )
                {
                    p->setValue( processedFeatures );
                }

                if ( p && p->wasCanceled() )
                {
                    break;
                }
                if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) )
                {
                    continue;
                }
                {
                    outputFeature.setAttributes( currentFeature.attributes() );
                    first = false;
                }
                dissolveFeature( currentFeature, processedFeatures, &dissolveGeometry );
                ++processedFeatures;
                ++jt;
            }
        }
        outputFeature.setGeometry( dissolveGeometry );
        vWriter.addFeature( outputFeature );
    }
    return true;
}
void QgsDelimitedTextProvider::rescanFile() const
{
  mRescanRequired = false;
  resetIndexes();

  bool buildSpatialIndex = nullptr != mSpatialIndex;
  bool buildSubsetIndex = mBuildSubsetIndex && ( mSubsetExpression || mGeomRep != GeomNone );

  // In case file has been rewritten check that it is still valid

  mValid = mLayerValid && mFile->isValid();
  if ( ! mValid )
    return;

  // Open the file and get number of rows, etc. We assume that the
  // file has a header row and process accordingly. Caller should make
  // sure that the delimited file is properly formed.

  QStringList messages;

  if ( mGeomRep == GeomAsWkt )
  {
    mWktFieldIndex = mFile->fieldIndex( mWktFieldName );
    if ( mWktFieldIndex < 0 )
    {
      messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "Wkt" ), mWktFieldName ) );
    }
  }
  else if ( mGeomRep == GeomAsXy )
  {
    mXFieldIndex = mFile->fieldIndex( mXFieldName );
    mYFieldIndex = mFile->fieldIndex( mYFieldName );
    if ( mXFieldIndex < 0 )
    {
      messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "X" ), mWktFieldName ) );
    }
    if ( mYFieldIndex < 0 )
    {
      messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "Y" ), mWktFieldName ) );
    }
  }
  if ( !messages.isEmpty() )
  {
    reportErrors( messages );
    QgsDebugMsg( "Delimited text source invalid on rescan - missing geometry fields" );
    mValid = false;
    return;
  }

  // Reset the field columns

  for ( int i = 0; i < attributeFields.size(); i++ )
  {
    attributeColumns[i] = mFile->fieldIndex( attributeFields.at( i ).name() );
  }

  // Scan through the features in the file

  mSubsetIndex.clear();
  mUseSubsetIndex = false;
  QgsFeatureIterator fi = getFeatures( QgsFeatureRequest() );
  mNumberFeatures = 0;
  mExtent = QgsRectangle();
  QgsFeature f;
  bool foundFirstGeometry = false;
  while ( fi.nextFeature( f ) )
  {
    if ( mGeometryType != QgsWkbTypes::NullGeometry && f.hasGeometry() )
    {
      if ( !foundFirstGeometry )
      {
        mExtent = f.geometry().boundingBox();
        foundFirstGeometry = true;
      }
      else
      {
        QgsRectangle bbox( f.geometry().boundingBox() );
        mExtent.combineExtentWith( bbox );
      }
      if ( buildSpatialIndex )
        mSpatialIndex->insertFeature( f );
    }
    if ( buildSubsetIndex )
      mSubsetIndex.append( ( quintptr ) f.id() );
    mNumberFeatures++;
  }
  if ( buildSubsetIndex )
  {
    long recordCount = mFile->recordCount();
    recordCount -= recordCount / SUBSET_ID_THRESHOLD_FACTOR;
    mUseSubsetIndex = recordCount < mSubsetIndex.size();
    if ( ! mUseSubsetIndex )
      mSubsetIndex.clear();
  }

  mUseSpatialIndex = buildSpatialIndex;
}
bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer* eventLayer, int lineField, int eventField, QgsFeatureIds &unlocatedFeatureIds, const QString& outputLayer,
                                      const QString& outputFormat, int locationField1, int locationField2, int offsetField, double offsetScale,
                                      bool forceSingleGeometry, QgsVectorDataProvider* memoryProvider, QProgressDialog* p )
{
    if ( !lineLayer || !eventLayer || !lineLayer->isValid() || !eventLayer->isValid() )
    {
        return false;
    }

    //create line field / id map for line layer
    QMultiHash< QString, QgsFeature > lineLayerIdMap; //1:n possible (e.g. several linear reference geometries for one feature in the event layer)
    QgsFeatureIterator fit = lineLayer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() << lineField ) );
    QgsFeature fet;
    while ( fit.nextFeature( fet ) )
    {
        lineLayerIdMap.insert( fet.attribute( lineField ).toString(), fet );
    }

    //create output datasource or attributes in memory provider
    QgsVectorFileWriter* fileWriter = nullptr;
    QgsFeatureList memoryProviderFeatures;
    if ( !memoryProvider )
    {
        QGis::WkbType memoryProviderType = QGis::WKBMultiLineString;
        if ( locationField2 == -1 )
        {
            memoryProviderType = forceSingleGeometry ? QGis::WKBPoint : QGis::WKBMultiPoint;
        }
        else
        {
            memoryProviderType = forceSingleGeometry ? QGis::WKBLineString : QGis::WKBMultiLineString;
        }
        fileWriter = new QgsVectorFileWriter( outputLayer,
                                              eventLayer->dataProvider()->encoding(),
                                              eventLayer->fields(),
                                              memoryProviderType,
                                              &( lineLayer->crs() ),
                                              outputFormat );
    }
    else
    {
        memoryProvider->addAttributes( eventLayer->fields().toList() );
    }

    //iterate over eventLayer and write new features to output file or layer
    fit = eventLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) );
    QgsGeometry* lrsGeom = nullptr;
    double measure1, measure2 = 0.0;

    int nEventFeatures = eventLayer->featureCount();
    int featureCounter = 0;
    int nOutputFeatures = 0; //number of output features for the current event feature
    if ( p )
    {
        p->setWindowModality( Qt::WindowModal );
        p->setMinimum( 0 );
        p->setMaximum( nEventFeatures );
        p->show();
    }

    while ( fit.nextFeature( fet ) )
    {
        nOutputFeatures = 0;

        //update progress dialog
        if ( p )
        {
            if ( p->wasCanceled() )
            {
                break;
            }
            p->setValue( featureCounter );
            ++featureCounter;
        }

        measure1 = fet.attribute( locationField1 ).toDouble();
        if ( locationField2 != -1 )
        {
            measure2 = fet.attribute( locationField2 ).toDouble();
            if ( qgsDoubleNear(( measure2 - measure1 ), 0.0 ) )
            {
                continue;
            }
        }

        QList<QgsFeature> featureIdList = lineLayerIdMap.values( fet.attribute( eventField ).toString() );
        QList<QgsFeature>::iterator featureIdIt = featureIdList.begin();
        for ( ; featureIdIt != featureIdList.end(); ++featureIdIt )
        {
            if ( locationField2 == -1 )
            {
                lrsGeom = locateAlongMeasure( measure1, featureIdIt->constGeometry() );
            }
            else
            {
                lrsGeom = locateBetweenMeasures( measure1, measure2, featureIdIt->constGeometry() );
            }

            if ( lrsGeom )
            {
                ++nOutputFeatures;
                addEventLayerFeature( fet, lrsGeom, featureIdIt->geometry(), fileWriter, memoryProviderFeatures, offsetField, offsetScale, forceSingleGeometry );
            }
        }
        if ( nOutputFeatures < 1 )
        {
            unlocatedFeatureIds.insert( fet.id() );
        }
    }

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

    if ( memoryProvider )
    {
        memoryProvider->addFeatures( memoryProviderFeatures );
    }
    delete fileWriter;
    return true;
}
Example #21
0
int QgsAtlasComposition::updateFeatures()
{
  //needs to be called when layer, filter, sort changes

  if ( !mCoverageLayer )
  {
    return 0;
  }

  updateFilenameExpression();

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

  std::auto_ptr<QgsExpression> filterExpression;
  if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
  {
    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 && !mFeatureFilter.isEmpty() )
    {
      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( feat.id(), feat.attributes()[ mSortKeyAttributeIdx ] );
    }
  }

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

  QgsExpression::setSpecialColumn( "$numfeatures", QVariant(( int )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();
}
void QgsEditorWidgetWrapper::setFeature( const QgsFeature& feature )
{
  setValue( feature.attribute( mFieldIdx ) );
}
void QgsEditorWidgetWrapper::updateConstraint( const QgsVectorLayer *layer, int index, const QgsFeature &ft, QgsFieldConstraints::ConstraintOrigin constraintOrigin )
{
  QStringList errors;
  QStringList softErrors;
  QStringList expressions;
  QStringList descriptions;
  bool toEmit( false );
  bool hardConstraintsOk( true );
  bool softConstraintsOk( true );

  QgsField field = layer->fields().at( index );
  QString expression = field.constraints().constraintExpression();

  if ( ft.isValid() )
  {
    if ( ! expression.isEmpty() )
    {
      expressions << expression;
      descriptions << field.constraints().constraintDescription();
      toEmit = true;
    }

    if ( field.constraints().constraints() & QgsFieldConstraints::ConstraintNotNull )
    {
      descriptions << tr( "Not NULL" );
      if ( !expression.isEmpty() )
      {
        expressions << field.name() + QStringLiteral( " IS NOT NULL" );
      }
      else
      {
        expressions << QStringLiteral( "IS NOT NULL" );
      }
      toEmit = true;
    }

    if ( field.constraints().constraints() & QgsFieldConstraints::ConstraintUnique )
    {
      descriptions << tr( "Unique" );
      if ( !expression.isEmpty() )
      {
        expressions << field.name() + QStringLiteral( " IS UNIQUE" );
      }
      else
      {
        expressions << QStringLiteral( "IS UNIQUE" );
      }
      toEmit = true;
    }

    hardConstraintsOk = QgsVectorLayerUtils::validateAttribute( layer, ft, index, errors, QgsFieldConstraints::ConstraintStrengthHard, constraintOrigin );

    softConstraintsOk = QgsVectorLayerUtils::validateAttribute( layer, ft, index, softErrors, QgsFieldConstraints::ConstraintStrengthSoft, constraintOrigin );
    errors << softErrors;
  }
  else // invalid feature
  {
    if ( ! expression.isEmpty() )
    {
      hardConstraintsOk = true;
      softConstraintsOk = false;

      errors << QStringLiteral( "Invalid feature" );

      toEmit = true;
    }
  }

  mValidConstraint = hardConstraintsOk && softConstraintsOk;
  mIsBlockingCommit = !hardConstraintsOk;

  mConstraintFailureReason = errors.join( QStringLiteral( ", " ) );

  if ( toEmit )
  {
    QString errStr = errors.isEmpty() ? tr( "Constraint checks passed" ) : mConstraintFailureReason;

    QString description = descriptions.join( QStringLiteral( ", " ) );
    QString expressionDesc;
    if ( expressions.size() > 1 )
      expressionDesc = "( " + expressions.join( QStringLiteral( " ) AND ( " ) ) + " )";
    else if ( !expressions.isEmpty() )
      expressionDesc = expressions.at( 0 );

    ConstraintResult result = !hardConstraintsOk ? ConstraintResultFailHard
                              : ( !softConstraintsOk ? ConstraintResultFailSoft : ConstraintResultPass );
    //set the constraint result
    mConstraintResult = result;
    updateConstraintWidgetStatus();
    emit constraintStatusChanged( expressionDesc, description, errStr, result );
  }
}
Example #24
0
/**
 * This method is an extension of the constructor. It was implemented to reduce the amount of code duplicated between the constructors.
 */
bool eVisGenericEventBrowserGui::initBrowser()
{

  //setup gui
  setWindowTitle( tr( "Generic Event Browser" ) );

  connect( treeEventData, SIGNAL( itemDoubleClicked( QTreeWidgetItem *, int ) ), this, SLOT( launchExternalApplication( QTreeWidgetItem *, int ) ) );

  mHighlightSymbol.load( QStringLiteral( ":/evis/eVisHighlightSymbol.png" ) );
  mPointerSymbol.load( QStringLiteral( ":/evis/eVisPointerSymbol.png" ) );
  mCompassOffset = 0.0;

  //Flag to let us know if the browser fully loaded
  mBrowserInitialized = false;

  //Initialize some class variables
  mDefaultEventImagePathField = 0;
  mDefaultCompassBearingField = 0;
  mDefaultCompassOffsetField = 0;

  //initialize Display tab GUI elements
  pbtnNext->setEnabled( false );
  pbtnPrevious->setEnabled( false );


  //Set up Attribute display
  treeEventData->setColumnCount( 2 );
  QStringList treeHeaders;
  treeHeaders << tr( "Field" ) << tr( "Value" );
  treeEventData->setHeaderLabels( treeHeaders );

  //Initialize Options tab GUI elements
  cboxEventImagePathField->setEnabled( true );
  chkboxEventImagePathRelative->setChecked( false );

  chkboxDisplayCompassBearing->setChecked( false );
  cboxCompassBearingField->setEnabled( true );

  rbtnManualCompassOffset->setChecked( false );
  dsboxCompassOffset->setEnabled( true );
  dsboxCompassOffset->setValue( 0.0 );
  rbtnAttributeCompassOffset->setChecked( false );
  cboxCompassOffsetField->setEnabled( true );


  chkboxUseOnlyFilename->setChecked( false );

  QString myThemePath = QgsApplication::activeThemePath();
  pbtnResetEventImagePathData->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) );
  pbtnResetCompassBearingData->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) );
  pbtnResetCompassOffsetData->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) );
  pbtnResetBasePathData->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) );
  pbtnResetUseOnlyFilenameData->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) );
  pbtnResetApplyPathRulesToDocs->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) );

  chkboxSaveEventImagePathData->setChecked( false );
  chkboxSaveCompassBearingData->setChecked( false );
  chkboxSaveCompassOffsetData->setChecked( false );
  chkboxSaveBasePathData->setChecked( false );
  chkboxSaveUseOnlyFilenameData->setChecked( false );

  //Set up Configure External Application buttons
  pbtnAddFileType->setIcon( QIcon( QPixmap( myThemePath + "/mActionNewAttribute.svg" ) ) );
  pbtnDeleteFileType->setIcon( QIcon( QPixmap( myThemePath + "/mActionDeleteAttribute.svg" ) ) );

  //Check to for interface, not null when launched from plugin toolbar, otherwise expect map canvas
  if ( mInterface )
  {
    //check for active layer
    if ( mInterface->activeLayer() )
    {
      //verify that the active layer is a vector layer
      if ( QgsMapLayer::VectorLayer == mInterface->activeLayer()->type() )
      {
        mVectorLayer = ( QgsVectorLayer* )mInterface->activeLayer();
        mCanvas = mInterface->mapCanvas();
      }
      else
      {
        QMessageBox::warning( this, tr( "Warning" ), tr( "This tool only supports vector data" ) );
        return false;
      }
    }
    else
    {
      QMessageBox::warning( this, tr( "Warning" ), tr( "No active layers found" ) );
      return false;
    }
  }
  //check for map canvas, if map canvas is null, throw error
  else if ( mCanvas )
  {
    //check for active layer
    if ( mCanvas->currentLayer() )
    {
      //verify that the active layer is a vector layer
      if ( QgsMapLayer::VectorLayer == mCanvas->currentLayer()->type() )
      {
        mVectorLayer = ( QgsVectorLayer* )mCanvas->currentLayer();
      }
      else
      {
        QMessageBox::warning( this, tr( "Warning" ), tr( "This tool only supports vector data" ) );
        return false;
      }
    }
    else
    {
      QMessageBox::warning( this, tr( "Warning" ), tr( "No active layers found" ) );
      return false;
    }
  }
  else
  {
    QMessageBox::warning( this, tr( "Error" ), tr( "Unable to connect to either the map canvas or application interface" ) );
    return false;
  }

  //Connect rendering routine for highlighting symbols and load symbols
  connect( mCanvas, SIGNAL( renderComplete( QPainter * ) ), this, SLOT( renderSymbol( QPainter * ) ) );

  mDataProvider = mVectorLayer->dataProvider();

  /*
   * A list of the selected feature ids is made so that we can move forward and backward through
   * the list. The data providers only have the ability to get one feature at a time or
   * sequentially move forward through the selected features
   */
  if ( 0 == mVectorLayer->selectedFeatureCount() ) //if nothing is selected select everything
  {
    mVectorLayer->invertSelection();
    mFeatureIds = mVectorLayer->selectedFeatureIds().toList();
  }
  else //use selected features
  {
    mFeatureIds = mVectorLayer->selectedFeatureIds().toList();
  }

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

  //get the first feature in the list so we can set the field in the pulldown menues
  QgsFeature* myFeature = featureAtId( mFeatureIds.at( mCurrentFeatureIndex ) );
  if ( !myFeature )
  {
    QMessageBox::warning( this, tr( "Error" ), tr( "An invalid feature was received during initialization" ) );
    return false;
  }

  QgsFields myFields = mDataProvider->fields();
  mIgnoreEvent = true; //Ignore indexChanged event when adding items to combo boxes
  for ( int x = 0; x < myFields.count(); x++ )
  {
    QString name = myFields.at( x ).name();
    cboxEventImagePathField->addItem( name );
    cboxCompassBearingField->addItem( name );
    cboxCompassOffsetField->addItem( name );
    if ( myFeature->attribute( x ).toString().contains( QRegExp( "(jpg|jpeg|tif|tiff|gif)", Qt::CaseInsensitive ) ) )
    {
      mDefaultEventImagePathField = x;
    }

    if ( name.contains( QRegExp( "(comp|bear)", Qt::CaseInsensitive ) ) )
    {
      mDefaultCompassBearingField = x;
    }

    if ( name.contains( QRegExp( "(offset|declination)", Qt::CaseInsensitive ) ) )
    {
      mDefaultCompassOffsetField = x;
    }
  }
  mIgnoreEvent = false;

  //Set Display tab gui items
  if ( mFeatureIds.size() > 1 )
  {
    pbtnNext->setEnabled( true );
  }

  setWindowTitle( tr( "Event Browser - Displaying records 01 of %1" ).arg( mFeatureIds.size(), 2, 10, QChar( '0' ) ) );

  //Set Options tab gui items
  initOptionsTab();

  //Load file associations into Configure External Applications tab gui items
  QSettings myQSettings;
  myQSettings.beginWriteArray( QStringLiteral( "/eVis/filetypeassociations" ) );
  int myTotalAssociations = myQSettings.childGroups().count();
  int myIterator = 0;
  while ( myIterator < myTotalAssociations )
  {
    myQSettings.setArrayIndex( myIterator );
    tableFileTypeAssociations->insertRow( tableFileTypeAssociations->rowCount() );
    tableFileTypeAssociations->setItem( myIterator, 0, new QTableWidgetItem( myQSettings.value( QStringLiteral( "extension" ), "" ).toString() ) );
    tableFileTypeAssociations->setItem( myIterator, 1, new QTableWidgetItem( myQSettings.value( QStringLiteral( "application" ), "" ).toString() ) );
    myIterator++;
  }
  myQSettings.endArray();

  mBrowserInitialized = true;

  return true;
}
void QgsLineVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, const QVector< QgsPoint >& additionalPoints,
    QVector< QgsPoint >& tiedPoint ) const
{
  QgsVectorLayer *vl = mVectorLayer;

  if ( !vl )
    return;

  int featureCount = ( int ) vl->featureCount() * 2;
  int step = 0;

  QgsCoordinateTransform ct;
  ct.setSourceCrs( vl->crs() );
  if ( builder->coordinateTransformationEnabled() )
  {
    ct.setDestinationCrs( builder->destinationCrs() );
  }
  else
  {
    ct.setDestinationCrs( vl->crs() );
  }

  tiedPoint = QVector< QgsPoint >( additionalPoints.size(), QgsPoint( 0.0, 0.0 ) );

  TiePointInfo tmpInfo;
  tmpInfo.mLength = std::numeric_limits<double>::infinity();

  QVector< TiePointInfo > pointLengthMap( additionalPoints.size(), tmpInfo );
  QVector< TiePointInfo >::iterator pointLengthIt;

  //Graph's points;
  QVector< QgsPoint > points;

  QgsFeatureIterator fit = vl->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) );

  // begin: tie points to the graph
  QgsAttributeList la;
  QgsFeature feature;
  while ( fit.nextFeature( feature ) )
  {
    QgsMultiPolyline mpl;
    if ( QgsWkbTypes::flatType( feature.geometry().geometry()->wkbType() ) == QgsWkbTypes::MultiLineString )
      mpl = feature.geometry().asMultiPolyline();
    else if ( QgsWkbTypes::flatType( feature.geometry().geometry()->wkbType() ) == QgsWkbTypes::LineString )
      mpl.push_back( feature.geometry().asPolyline() );

    QgsMultiPolyline::iterator mplIt;
    for ( mplIt = mpl.begin(); mplIt != mpl.end(); ++mplIt )
    {
      QgsPoint pt1, pt2;
      bool isFirstPoint = true;
      QgsPolyline::iterator pointIt;
      for ( pointIt = mplIt->begin(); pointIt != mplIt->end(); ++pointIt )
      {
        pt2 = ct.transform( *pointIt );
        points.push_back( pt2 );

        if ( !isFirstPoint )
        {
          int i = 0;
          for ( i = 0; i != additionalPoints.size(); ++i )
          {
            TiePointInfo info;
            if ( pt1 == pt2 )
            {
              info.mLength = additionalPoints[ i ].sqrDist( pt1 );
              info.mTiedPoint = pt1;
            }
            else
            {
              info.mLength = additionalPoints[ i ].sqrDistToSegment( pt1.x(), pt1.y(),
                             pt2.x(), pt2.y(), info.mTiedPoint );
            }

            if ( pointLengthMap[ i ].mLength > info.mLength )
            {
              Q_UNUSED( info.mTiedPoint );
              info.mFirstPoint = pt1;
              info.mLastPoint = pt2;

              pointLengthMap[ i ] = info;
              tiedPoint[ i ] = info.mTiedPoint;
            }
          }
        }
        pt1 = pt2;
        isFirstPoint = false;
      }
    }
    emit buildProgress( ++step, featureCount );
  }
  // end: tie points to graph

  // add tied point to graph
  int i = 0;
  for ( i = 0; i < tiedPoint.size(); ++i )
  {
    if ( tiedPoint[ i ] != QgsPoint( 0.0, 0.0 ) )
    {
      points.push_back( tiedPoint [ i ] );
    }
  }

  QgsPointCompare pointCompare( builder->topologyTolerance() );

  qSort( points.begin(), points.end(), pointCompare );
  QVector< QgsPoint >::iterator tmp = std::unique( points.begin(), points.end() );
  points.resize( tmp - points.begin() );

  for ( i = 0;i < points.size();++i )
    builder->addVertex( i, points[ i ] );

  for ( i = 0; i < tiedPoint.size() ; ++i )
    tiedPoint[ i ] = *( my_binary_search( points.begin(), points.end(), tiedPoint[ i ], pointCompare ) );

  qSort( pointLengthMap.begin(), pointLengthMap.end(), TiePointInfoCompare );

  {
    // fill attribute list 'la'
    QgsAttributeList tmpAttr;
    if ( mDirectionFieldId != -1 )
    {
      tmpAttr.push_back( mDirectionFieldId );
    }

    QList< QgsArcProperter* >::const_iterator it;
    QgsAttributeList::const_iterator it2;

    for ( it = mProperterList.begin(); it != mProperterList.end(); ++it )
    {
      QgsAttributeList tmp = ( *it )->requiredAttributes();
      for ( it2 = tmp.begin(); it2 != tmp.end(); ++it2 )
      {
        tmpAttr.push_back( *it2 );
      }
    }
    qSort( tmpAttr.begin(), tmpAttr.end() );

    int lastAttrId = -1;
    for ( it2 = tmpAttr.begin(); it2 != tmpAttr.end(); ++it2 )
    {
      if ( *it2 == lastAttrId )
      {
        continue;
      }

      la.push_back( *it2 );

      lastAttrId = *it2;
    }
  } // end fill attribute list 'la'

  // begin graph construction
  fit = vl->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( la ) );
  while ( fit.nextFeature( feature ) )
  {
    int directionType = mDefaultDirection;

    // What direction have feature?
    QString str = feature.attribute( mDirectionFieldId ).toString();
    if ( str == mBothDirectionValue )
    {
      directionType = 3;
    }
    else if ( str == mDirectDirectionValue )
    {
      directionType = 1;
    }
    else if ( str == mReverseDirectionValue )
    {
      directionType = 2;
    }

    // begin features segments and add arc to the Graph;
    QgsMultiPolyline mpl;
    if ( QgsWkbTypes::flatType( feature.geometry().geometry()->wkbType() ) == QgsWkbTypes::MultiLineString )
      mpl = feature.geometry().asMultiPolyline();
    else if ( QgsWkbTypes::flatType( feature.geometry().geometry()->wkbType() ) == QgsWkbTypes::LineString )
      mpl.push_back( feature.geometry().asPolyline() );

    QgsMultiPolyline::iterator mplIt;
    for ( mplIt = mpl.begin(); mplIt != mpl.end(); ++mplIt )
    {
      QgsPoint pt1, pt2;

      bool isFirstPoint = true;
      QgsPolyline::iterator pointIt;
      for ( pointIt = mplIt->begin(); pointIt != mplIt->end(); ++pointIt )
      {
        pt2 = ct.transform( *pointIt );

        if ( !isFirstPoint )
        {
          QMap< double, QgsPoint > pointsOnArc;
          pointsOnArc[ 0.0 ] = pt1;
          pointsOnArc[ pt1.sqrDist( pt2 )] = pt2;

          TiePointInfo t;
          t.mFirstPoint = pt1;
          t.mLastPoint  = pt2;
          t.mLength = 0.0;
          pointLengthIt = my_binary_search( pointLengthMap.begin(), pointLengthMap.end(), t, TiePointInfoCompare );

          if ( pointLengthIt != pointLengthMap.end() )
          {
            QVector< TiePointInfo >::iterator it;
            for ( it = pointLengthIt; it - pointLengthMap.begin() >= 0; --it )
            {
              if ( it->mFirstPoint == pt1 && it->mLastPoint == pt2 )
              {
                pointsOnArc[ pt1.sqrDist( it->mTiedPoint )] = it->mTiedPoint;
              }
            }
            for ( it = pointLengthIt + 1; it != pointLengthMap.end(); ++it )
            {
              if ( it->mFirstPoint == pt1 && it->mLastPoint == pt2 )
              {
                pointsOnArc[ pt1.sqrDist( it->mTiedPoint )] = it->mTiedPoint;
              }
            }
          }

          QMap< double, QgsPoint >::iterator pointsIt;
          QgsPoint pt1;
          QgsPoint pt2;
          int pt1idx = -1, pt2idx = -1;
          bool isFirstPoint = true;
          for ( pointsIt = pointsOnArc.begin(); pointsIt != pointsOnArc.end(); ++pointsIt )
          {
            pt2 = *pointsIt;
            tmp = my_binary_search( points.begin(), points.end(), pt2, pointCompare );
            pt2 = *tmp;
            pt2idx = tmp - points.begin();

            if ( !isFirstPoint && pt1 != pt2 )
            {
              double distance = builder->distanceArea()->measureLine( pt1, pt2 );
              QVector< QVariant > prop;
              QList< QgsArcProperter* >::const_iterator it;
              for ( it = mProperterList.begin(); it != mProperterList.end(); ++it )
              {
                prop.push_back(( *it )->property( distance, feature ) );
              }

              if ( directionType == 1 ||
                   directionType == 3 )
              {
                builder->addArc( pt1idx, pt1, pt2idx, pt2, prop );
              }
              if ( directionType == 2 ||
                   directionType == 3 )
              {
                builder->addArc( pt2idx, pt2, pt1idx, pt1, prop );
              }
            }
            pt1idx = pt2idx;
            pt1 = pt2;
            isFirstPoint = false;
          }
        } // if ( !isFirstPoint )
        pt1 = pt2;
        isFirstPoint = false;
      } // for (it = pl.begin(); it != pl.end(); ++it)
    }
    emit buildProgress( ++step, featureCount );
  } // while( vl->nextFeature(feature) )
} // makeGraph( QgsGraphBuilderInterface *builder, const QVector< QgsPoint >& additionalPoints, QVector< QgsPoint >& tiedPoint )