QgsFeature QgsConvexHullAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback *feedback )
{
  QgsFeature f = feature;
  if ( f.hasGeometry() )
  {
    QgsGeometry outputGeometry = f.geometry().convexHull();
    if ( !outputGeometry )
      feedback->reportError( outputGeometry.lastError() );
    f.setGeometry( outputGeometry );
    if ( outputGeometry )
    {
      QgsAttributes attrs = f.attributes();
      attrs << outputGeometry.constGet()->area()
            << outputGeometry.constGet()->perimeter();
      f.setAttributes( attrs );
    }
    else
    {
      QgsAttributes attrs = f.attributes();
      attrs << QVariant()
            << QVariant();
      f.setAttributes( attrs );
    }
  }
  return f;
}
QgsFeatureList QgsConvexHullAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback *feedback )
{
  QgsFeature f = feature;
  if ( f.hasGeometry() )
  {
    QgsGeometry outputGeometry;
    if ( QgsWkbTypes::flatType( f.geometry().wkbType() ) == QgsWkbTypes::Point )
    {
      feedback->reportError( QObject::tr( "Cannot calculate convex hull for a single Point feature (try 'Minimum bounding geometry' algorithm instead)." ) );
      f.clearGeometry();
    }
    else
    {
      outputGeometry = f.geometry().convexHull();
      if ( outputGeometry.isNull() )
        feedback->reportError( outputGeometry.lastError() );
      f.setGeometry( outputGeometry );
    }
    if ( !outputGeometry.isNull() )
    {
      QgsAttributes attrs = f.attributes();
      attrs << outputGeometry.constGet()->area()
            << outputGeometry.constGet()->perimeter();
      f.setAttributes( attrs );
    }
    else
    {
      QgsAttributes attrs = f.attributes();
      attrs << QVariant()
            << QVariant();
      f.setAttributes( attrs );
    }
  }
  return QgsFeatureList() << f;
}
Beispiel #3
0
Qt3DRender::QGeometryRenderer *QgsLine3DSymbolEntityNode::renderer( const Qgs3DMapSettings &map, const QgsLine3DSymbol &symbol, const QgsVectorLayer *layer, const QgsFeatureRequest &request )
{
  QgsPointXY origin( map.origin().x(), map.origin().y() );

  // TODO: configurable
  int nSegments = 4;
  QgsGeometry::EndCapStyle endCapStyle = QgsGeometry::CapRound;
  QgsGeometry::JoinStyle joinStyle = QgsGeometry::JoinStyleRound;
  double mitreLimit = 0;

  QList<QgsPolygon *> polygons;
  QgsFeature f;
  QgsFeatureIterator fi = layer->getFeatures( request );
  while ( fi.nextFeature( f ) )
  {
    if ( f.geometry().isNull() )
      continue;

    QgsGeometry geom = f.geometry();

    // segmentize curved geometries if necessary
    if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
      geom = QgsGeometry( geom.constGet()->segmentize() );

    const QgsAbstractGeometry *g = geom.constGet();

    QgsGeos engine( g );
    QgsAbstractGeometry *buffered = engine.buffer( symbol.width() / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory

    if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::Polygon )
    {
      QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered );
      Qgs3DUtils::clampAltitudes( polyBuffered, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), map );
      polygons.append( polyBuffered );
    }
    else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::MultiPolygon )
    {
      QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered );
      for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i )
      {
        QgsAbstractGeometry *partBuffered = mpolyBuffered->geometryN( i );
        Q_ASSERT( QgsWkbTypes::flatType( partBuffered->wkbType() ) == QgsWkbTypes::Polygon );
        QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( partBuffered )->clone(); // need to clone individual geometry parts
        Qgs3DUtils::clampAltitudes( polyBuffered, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), map );
        polygons.append( polyBuffered );
      }
      delete buffered;
    }
  }

  mGeometry = new QgsTessellatedPolygonGeometry;
  mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight() );

  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
  renderer->setGeometry( mGeometry );

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

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

  // Fix error
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else if ( method == RemoveHoles )
  {
    deleteFeatureGeometryRing( error->layerId(), feature, vidx.part, vidx.ring, changes );
    error->setFixed( method );
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}
Beispiel #5
0
void QgsBufferedLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
{
  if ( f.geometry().isNull() )
    return;

  LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;

  QgsGeometry geom = f.geometry();

  // segmentize curved geometries if necessary
  if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
    geom = QgsGeometry( geom.constGet()->segmentize() );

  const QgsAbstractGeometry *g = geom.constGet();

  // TODO: configurable
  const int nSegments = 4;
  const QgsGeometry::EndCapStyle endCapStyle = QgsGeometry::CapRound;
  const QgsGeometry::JoinStyle joinStyle = QgsGeometry::JoinStyleRound;
  const double mitreLimit = 0;

  QgsGeos engine( g );
  QgsAbstractGeometry *buffered = engine.buffer( mSymbol.width() / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory

  if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::Polygon )
  {
    QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered );
    Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() );
    out.polygons.append( polyBuffered );
    out.fids.append( f.id() );
  }
  else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::MultiPolygon )
  {
    QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered );
    for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i )
    {
      QgsAbstractGeometry *partBuffered = mpolyBuffered->geometryN( i );
      Q_ASSERT( QgsWkbTypes::flatType( partBuffered->wkbType() ) == QgsWkbTypes::Polygon );
      QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( partBuffered )->clone(); // need to clone individual geometry parts
      Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() );
      out.polygons.append( polyBuffered );
      out.fids.append( f.id() );
    }
    delete buffered;
  }
}
Beispiel #6
0
void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const
{
  QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ];
  QgsFeature feature;
  if ( !featurePool->get( error->featureId(), feature ) )
  {
    error->setObsolete();
    return;
  }
  double layerToMapUnits = featurePool->getLayerToMapUnits();
  QgsGeometry g = feature.geometry();
  const QgsAbstractGeometry *geom = g.constGet();
  QgsVertexId vidx = error->vidx();

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

  // Check if error still applies
  double value;
  if ( !checkThreshold( layerToMapUnits, QgsGeometryCheckerUtils::getGeomPart( geom, vidx.part ), value ) )
  {
    error->setObsolete();
    return;
  }

  // Fix with selected method
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else if ( method == Delete )
  {
    deleteFeatureGeometryPart( error->layerId(), feature, vidx.part, changes );
    error->setFixed( method );
  }
  else if ( method == MergeLongestEdge || method == MergeLargestArea || method == MergeIdenticalAttribute )
  {
    QString errMsg;
    if ( mergeWithNeighbor( error->layerId(), feature,  vidx.part, method, mergeAttributeIndices[error->layerId()], changes, errMsg ) )
    {
      error->setFixed( method );
    }
    else
    {
      error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) );
    }
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}
QList<QgsSingleGeometryCheckError *> QgsGeometryMultipartCheck::processGeometry( const QgsGeometry &geometry ) const
{
  QList<QgsSingleGeometryCheckError *> errors;

  const QgsAbstractGeometry *geom = geometry.constGet();
  QgsWkbTypes::Type type = geom->wkbType();
  if ( geom->partCount() == 1 && QgsWkbTypes::isMultiType( type ) )
  {
    errors.append( new QgsSingleGeometryCheckError( this, geometry, geometry ) );
  }
  return errors;
}
void QgsExpressionSelectionDialog::mButtonZoomToFeatures_clicked()
{
  if ( mExpressionBuilder->expressionText().isEmpty() || !mMapCanvas )
    return;

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

  QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( mExpressionBuilder->expressionText() )
                              .setExpressionContext( context )
                              .setNoAttributes();

  QgsFeatureIterator features = mLayer->getFeatures( request );

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

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

  QgsSettings settings;
  int timeout = settings.value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
  if ( featureCount > 0 )
  {
    mMapCanvas->zoomToFeatureExtent( bbox );
    if ( mMessageBar )
    {
      mMessageBar->pushMessage( QString(),
                                tr( "Zoomed to %n matching feature(s)", "number of matching features", featureCount ),
                                Qgis::Info,
                                timeout );
    }
  }
  else if ( mMessageBar )
  {
    mMessageBar->pushMessage( QString(),
                              tr( "No matching features found" ),
                              Qgis::Info,
                              timeout );
  }
  saveRecent();
}
Beispiel #9
0
QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId featureId, int vertex )
{
  if ( !mLayer->isSpatial() )
    return QgsVectorLayer::InvalidLayer;

  QgsFeature f;
  if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
    return QgsVectorLayer::FetchFeatureFailed; // geometry not found

  QgsGeometry geometry = f.geometry();

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

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

  mLayer->editBuffer()->changeGeometry( featureId, geometry );
  return !geometry.isNull() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry;
}
void QgsGeometryMultipartCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
  QgsFeaturePool *featurePool = featurePools[ error->layerId() ];
  QgsFeature feature;
  if ( !featurePool->getFeature( error->featureId(), feature ) )
  {
    error->setObsolete();
    return;
  }
  QgsGeometry featureGeom = feature.geometry();
  const QgsAbstractGeometry *geom = featureGeom.constGet();

  // Check if error still applies
  if ( geom->partCount() > 1 || !QgsWkbTypes::isMultiType( geom->wkbType() ) )
  {
    error->setObsolete();
    return;
  }

  // Fix error
  if ( method == NoChange )
  {
    error->setFixed( method );
  }
  else if ( method == ConvertToSingle )
  {
    feature.setGeometry( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, 0 )->clone() ) );
    featurePool->updateFeature( feature );
    error->setFixed( method );
    changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
  }
  else if ( method == RemoveObject )
  {
    featurePool->deleteFeature( feature.id() );
    error->setFixed( method );
    changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeRemoved ) );
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}
Beispiel #11
0
QgsFeatureList QgsBoundaryAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback *feedback )
{
  QgsFeature outFeature = feature;

  if ( feature.hasGeometry() )
  {
    QgsGeometry inputGeometry = feature.geometry();
    QgsGeometry outputGeometry = QgsGeometry( inputGeometry.constGet()->boundary() );
    if ( !outputGeometry )
    {
      feedback->reportError( QObject::tr( "No boundary for feature %1 (possibly a closed linestring?)'" ).arg( feature.id() ) );
      outFeature.clearGeometry();
    }
    else
    {
      outFeature.setGeometry( outputGeometry );
    }
  }
  return QgsFeatureList() << outFeature;
}
Beispiel #12
0
void QgsSimpleLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
{
  if ( f.geometry().isNull() )
    return;

  LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;

  QgsPoint centroid;
  if ( mSymbol.altitudeBinding() == Qgs3DTypes::AltBindCentroid )
    centroid = QgsPoint( f.geometry().centroid().asPoint() );

  QgsGeometry geom = f.geometry();
  const QgsAbstractGeometry *g = geom.constGet();
  if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
  {
    for ( int i = 0; i < ls->vertexCount(); ++i )
    {
      QgsPoint p = ls->pointN( i );
      float z = Qgs3DUtils::clampAltitude( p, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), centroid, context.map() );
      out.vertices << QVector3D( p.x() - context.map().origin().x(), z, -( p.y() - context.map().origin().y() ) );
      out.indexes << out.vertices.count() - 1;
    }
  }
  else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
  {
    for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
    {
      const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
      for ( int i = 0; i < ls->vertexCount(); ++i )
      {
        QgsPoint p = ls->pointN( i );
        float z = Qgs3DUtils::clampAltitude( p, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), centroid, context.map() );
        out.vertices << QVector3D( p.x() - context.map().origin().x(), z, -( p.y() - context.map().origin().y() ) );
        out.indexes << out.vertices.count() - 1;
      }
      out.indexes << 0;  // add primitive restart
    }
  }

  out.indexes << 0;  // add primitive restart
}
QgsFeatureList QgsLineSubstringAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback * )
{
  QgsFeature f = feature;
  if ( f.hasGeometry() )
  {
    const QgsGeometry geometry = f.geometry();
    double startDistance = mStartDistance;
    if ( mDynamicStartDistance )
      startDistance = mStartDistanceProperty.valueAsDouble( context.expressionContext(), startDistance );

    double endDistance = mEndDistance;
    if ( mDynamicEndDistance )
      endDistance = mEndDistanceProperty.valueAsDouble( context.expressionContext(), endDistance );

    const QgsCurve *curve = nullptr;
    if ( !geometry.isMultipart() )
      curve = qgsgeometry_cast< const QgsCurve * >( geometry.constGet() );
    else
    {
      if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geometry.constGet() ) )
      {
        if ( collection->numGeometries() > 0 )
        {
          curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
        }
      }
    }
    if ( curve )
    {
      std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
      QgsGeometry result( std::move( substring ) );
      f.setGeometry( result );
    }
    else
    {
      f.clearGeometry();
    }
  }
  return QgsFeatureList() << f;
}
QVariantMap QgsSplitWithLinesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
  std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
  if ( !source )
    throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          }
          inGeoms = outGeoms;
        }
      }
    }

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

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

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

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

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

    feedback->setProgress( i * step );
  }

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

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

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

  QgsAttributeList fieldIndicesA = QgsProcessingUtils::fieldNamesToIndices( fieldsA, sourceA->fields() );
  QgsAttributeList fieldIndicesB = QgsProcessingUtils::fieldNamesToIndices( fieldsB, sourceB->fields() );

  QString intersectFieldsPrefix = parameterAsString( parameters, QStringLiteral( "INTERSECT_FIELDS_PREFIX" ), context );
  QgsFields outFields = QgsProcessingUtils::combineFields(
                          QgsProcessingUtils::indicesToFields( fieldIndicesA, sourceA->fields() ),
                          QgsProcessingUtils::indicesToFields( fieldIndicesB, sourceB->fields() ),
                          intersectFieldsPrefix );

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

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

    if ( !inFeatureA.hasGeometry() )
      continue;

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

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

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

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

    feedback->setProgress( i * step );

  }

  QVariantMap outputs;
  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
  return outputs;
}
QgsInternalGeometryEngine::QgsInternalGeometryEngine( const QgsGeometry &geometry )
  : mGeometry( geometry.constGet() )
{

}
Beispiel #17
0
QString QgsExpression::formatPreviewString( const QVariant &value )
{
  static const int MAX_PREVIEW = 60;

  if ( value.canConvert<QgsGeometry>() )
  {
    //result is a geometry
    QgsGeometry geom = value.value<QgsGeometry>();
    if ( geom.isNull() )
      return tr( "<i>&lt;empty geometry&gt;</i>" );
    else
      return tr( "<i>&lt;geometry: %1&gt;</i>" ).arg( QgsWkbTypes::displayString( geom.constGet()->wkbType() ) );
  }
  else if ( !value.isValid() )
  {
    return tr( "<i>NULL</i>" );
  }
  else if ( value.canConvert< QgsFeature >() )
  {
    //result is a feature
    QgsFeature feat = value.value<QgsFeature>();
    return tr( "<i>&lt;feature: %1&gt;</i>" ).arg( feat.id() );
  }
  else if ( value.canConvert< QgsInterval >() )
  {
    //result is a feature
    QgsInterval interval = value.value<QgsInterval>();
    return tr( "<i>&lt;interval: %1 days&gt;</i>" ).arg( interval.days() );
  }
  else if ( value.canConvert< QgsGradientColorRamp >() )
  {
    return tr( "<i>&lt;gradient ramp&gt;</i>" );
  }
  else if ( value.type() == QVariant::Date )
  {
    QDate dt = value.toDate();
    return tr( "<i>&lt;date: %1&gt;</i>" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd" ) ) );
  }
  else if ( value.type() == QVariant::Time )
  {
    QTime tm = value.toTime();
    return tr( "<i>&lt;time: %1&gt;</i>" ).arg( tm.toString( QStringLiteral( "hh:mm:ss" ) ) );
  }
  else if ( value.type() == QVariant::DateTime )
  {
    QDateTime dt = value.toDateTime();
    return tr( "<i>&lt;datetime: %1&gt;</i>" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd hh:mm:ss" ) ) );
  }
  else if ( value.type() == QVariant::String )
  {
    QString previewString = value.toString();
    if ( previewString.length() > MAX_PREVIEW + 3 )
    {
      return tr( "'%1…'" ).arg( previewString.left( MAX_PREVIEW ) );
    }
    else
    {
      return previewString.prepend( '\'' ).append( '\'' );
    }
  }
  else if ( value.type() == QVariant::Map )
  {
    QString mapStr;
    const QVariantMap map = value.toMap();
    for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
    {
      if ( !mapStr.isEmpty() ) mapStr.append( ", " );
      mapStr.append( it.key() ).append( ": " ).append( formatPreviewString( it.value() ) );
      if ( mapStr.length() > MAX_PREVIEW + 3 )
      {
        mapStr = QString( tr( "%1…" ) ).arg( mapStr.left( MAX_PREVIEW ) );
        break;
      }
    }
    return tr( "<i>&lt;map: %1&gt;</i>" ).arg( mapStr );
  }
  else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
  {
    QString listStr;
    const QVariantList list = value.toList();
    for ( QVariantList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
    {
      if ( !listStr.isEmpty() ) listStr.append( ", " );
      listStr.append( formatPreviewString( *it ) );
      if ( listStr.length() > MAX_PREVIEW + 3 )
      {
        listStr = QString( tr( "%1…" ) ).arg( listStr.left( MAX_PREVIEW ) );
        break;
      }
    }
    return tr( "<i>&lt;array: %1&gt;</i>" ).arg( listStr );
  }
  else
  {
    return value.toString();
  }
}
Beispiel #18
0
Qt3DRender::QGeometryRenderer *QgsLine3DSymbolEntityNode::rendererSimple( const Qgs3DMapSettings &map, const QgsLine3DSymbol &symbol, const QgsVectorLayer *layer, const QgsFeatureRequest &request )
{
  QVector<QVector3D> vertices;
  vertices << QVector3D();  // the first index is invalid, we use it for primitive restart
  QVector<unsigned int> indexes;

  QgsPoint centroid;
  QgsPointXY origin( map.origin().x(), map.origin().y() );
  QgsFeature f;
  QgsFeatureIterator fi = layer->getFeatures( request );
  while ( fi.nextFeature( f ) )
  {
    if ( f.geometry().isNull() )
      continue;

    if ( symbol.altitudeBinding() == AltBindCentroid )
      centroid = QgsPoint( f.geometry().centroid().asPoint() );

    QgsGeometry geom = f.geometry();
    const QgsAbstractGeometry *g = geom.constGet();
    if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
    {
      for ( int i = 0; i < ls->vertexCount(); ++i )
      {
        QgsPoint p = ls->pointN( i );
        float z = Qgs3DUtils::clampAltitude( p, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), centroid, map );
        vertices << QVector3D( p.x() - map.origin().x(), z, -( p.y() - map.origin().y() ) );
        indexes << vertices.count() - 1;
      }
    }
    else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
    {
      for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
      {
        const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
        for ( int i = 0; i < ls->vertexCount(); ++i )
        {
          QgsPoint p = ls->pointN( i );
          float z = Qgs3DUtils::clampAltitude( p, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), centroid, map );
          vertices << QVector3D( p.x() - map.origin().x(), z, -( p.y() - map.origin().y() ) );
          indexes << vertices.count() - 1;
        }
        indexes << 0;  // add primitive restart
      }
    }

    indexes << 0;  // add primitive restart
  }

  QByteArray vertexBufferData;
  vertexBufferData.resize( vertices.size() * 3 * sizeof( float ) );
  float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
  int idx = 0;
  for ( const auto &v : vertices )
  {
    rawVertexArray[idx++] = v.x();
    rawVertexArray[idx++] = v.y();
    rawVertexArray[idx++] = v.z();
  }

  QByteArray indexBufferData;
  indexBufferData.resize( indexes.size() * sizeof( int ) );
  unsigned int *rawIndexArray = reinterpret_cast<unsigned int *>( indexBufferData.data() );
  idx = 0;
  for ( unsigned int indexVal : indexes )
  {
    rawIndexArray[idx++] = indexVal;
  }

  Qt3DRender::QBuffer *vertexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this );
  vertexBuffer->setData( vertexBufferData );

  Qt3DRender::QBuffer *indexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::IndexBuffer, this );
  indexBuffer->setData( indexBufferData );

  Qt3DRender::QAttribute *positionAttribute = new Qt3DRender::QAttribute( this );
  positionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
  positionAttribute->setBuffer( vertexBuffer );
  positionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
  positionAttribute->setVertexSize( 3 );
  positionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );

  Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute( this );
  indexAttribute->setAttributeType( Qt3DRender::QAttribute::IndexAttribute );
  indexAttribute->setBuffer( indexBuffer );
  indexAttribute->setVertexBaseType( Qt3DRender::QAttribute::UnsignedInt );

  Qt3DRender::QGeometry *geom = new Qt3DRender::QGeometry;
  geom->addAttribute( positionAttribute );
  geom->addAttribute( indexAttribute );

  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
  renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStrip );
  renderer->setGeometry( geom );
  renderer->setVertexCount( vertices.count() );
  renderer->setPrimitiveRestartEnabled( true );
  renderer->setRestartIndexValue( 0 );
  return renderer;
}
Beispiel #19
0
void QgsMapToolAddPart::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
{
  //check if we operate on a vector layer
  QgsVectorLayer *vlayer = currentVectorLayer();
  if ( !vlayer )
  {
    notifyNotVectorLayer();
    return;
  }

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

  bool isGeometryEmpty = false;
  QgsFeatureList selectedFeatures = vlayer->selectedFeatures();
  if ( !selectedFeatures.isEmpty() && selectedFeatures.at( 0 ).geometry().isNull() )
    isGeometryEmpty = true;

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

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

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

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

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

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

        return;
      }

      if ( !isCapturing() )
        return;

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

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

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

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

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

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

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

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

      vlayer->endEditCommand();

      vlayer->triggerRepaint();

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

      return;
    }

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

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

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

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

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

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

  emit messageEmitted( errorMessage, Qgis::Warning );
  vlayer->destroyEditCommand();
}
Beispiel #20
0
void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeight )
{
  if ( _minimum_distance_between_coordinates( polygon ) < 0.001 )
  {
    // when the distances between coordinates of input points are very small,
    // the triangulation likes to crash on numerical errors - when the distances are ~ 1e-5
    // Assuming that the coordinates should be in a projected CRS, we should be able
    // to simplify geometries that may cause problems and avoid possible crashes
    QgsGeometry polygonSimplified = QgsGeometry( polygon.clone() ).simplify( 0.001 );
    const QgsPolygon *polygonSimplifiedData = qgsgeometry_cast<const QgsPolygon *>( polygonSimplified.constGet() );
    if ( _minimum_distance_between_coordinates( *polygonSimplifiedData ) < 0.001 )
    {
      // Failed to fix that. It could be a really tiny geometry... or maybe they gave us
      // geometry in unprojected lat/lon coordinates
      QgsMessageLog::logMessage( "geometry's coordinates are too close to each other and simplification failed - skipping", "3D" );
    }
    else
    {
      addPolygon( *polygonSimplifiedData, extrusionHeight );
    }
    return;
  }

  if ( !_check_intersecting_rings( polygon ) )
  {
    // skip the polygon - it would cause a crash inside poly2tri library
    QgsMessageLog::logMessage( "polygon rings intersect each other - skipping", "3D" );
    return;
  }

  const QgsCurve *exterior = polygon.exteriorRing();

  QList< std::vector<p2t::Point *> > polylinesToDelete;
  QHash<p2t::Point *, float> z;

  std::vector<p2t::Point *> polyline;

  const QVector3D pNormal = _calculateNormal( exterior, mOriginX, mOriginY );
  const int pCount = exterior->numPoints();

  // Polygon is a triangle
  if ( pCount == 4 )
  {
    QgsPoint pt;
    QgsVertexId::VertexType vt;
    for ( int i = 0; i < 3; i++ )
    {
      exterior->pointAt( i, pt, vt );
      mData << pt.x() - mOriginX << pt.z() << - pt.y() + mOriginY;
      if ( mAddNormals )
        mData << pNormal.x() << pNormal.z() << - pNormal.y();
    }
  }
  else
  {
    if ( !qgsDoubleNear( pNormal.length(), 1, 0.001 ) )
      return;  // this should not happen - pNormal should be normalized to unit length

    QVector3D pXVector, pYVector;
    _normalVectorToXYVectors( pNormal, pXVector, pYVector );

    // so now we have three orthogonal unit vectors defining new base
    // let's build transform matrix. We actually need just a 3x3 matrix,
    // but Qt does not have good support for it, so using 4x4 matrix instead.
    QMatrix4x4 toNewBase(
      pXVector.x(), pXVector.y(), pXVector.z(), 0,
      pYVector.x(), pYVector.y(), pYVector.z(), 0,
      pNormal.x(), pNormal.y(), pNormal.z(), 0,
      0, 0, 0, 0 );

    // our 3x3 matrix is orthogonal, so for inverse we only need to transpose it
    QMatrix4x4 toOldBase = toNewBase.transposed();

    const QgsPoint ptFirst( exterior->startPoint() );
    _ringToPoly2tri( exterior, ptFirst, toNewBase, polyline, z );
    polylinesToDelete << polyline;

    // TODO: robustness (no nearly duplicate points, invalid geometries ...)

    double x0 = ptFirst.x(), y0 = ptFirst.y(), z0 = ( std::isnan( ptFirst.z() ) ? 0 : ptFirst.z() );
    if ( polyline.size() == 3 && polygon.numInteriorRings() == 0 )
    {
      for ( std::vector<p2t::Point *>::iterator it = polyline.begin(); it != polyline.end(); it++ )
      {
        p2t::Point *p = *it;
        QVector4D ptInNewBase( p->x, p->y, z[p], 0 );
        QVector4D nPoint = toOldBase * ptInNewBase;
        const double fx = nPoint.x() - mOriginX + x0;
        const double fy = nPoint.y() - mOriginY + y0;
        const double fz = nPoint.z() + extrusionHeight + z0;
        mData << fx << fz << -fy;
        if ( mAddNormals )
          mData << pNormal.x() << pNormal.z() << - pNormal.y();
      }
    }
    else if ( polyline.size() >= 3 )
    {
      p2t::CDT *cdt = new p2t::CDT( polyline );

      // polygon holes
      for ( int i = 0; i < polygon.numInteriorRings(); ++i )
      {
        std::vector<p2t::Point *> holePolyline;
        const QgsCurve *hole = polygon.interiorRing( i );

        _ringToPoly2tri( hole, ptFirst, toNewBase, holePolyline, z );

        cdt->AddHole( holePolyline );
        polylinesToDelete << holePolyline;
      }

      try
      {
        cdt->Triangulate();

        std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();

        for ( size_t i = 0; i < triangles.size(); ++i )
        {
          p2t::Triangle *t = triangles[i];
          for ( int j = 0; j < 3; ++j )
          {
            p2t::Point *p = t->GetPoint( j );
            QVector4D ptInNewBase( p->x, p->y, z[p], 0 );
            QVector4D nPoint = toOldBase * ptInNewBase;
            const double fx = nPoint.x() - mOriginX + x0;
            const double fy = nPoint.y() - mOriginY + y0;
            const double fz = nPoint.z() + extrusionHeight + z0;
            mData << fx << fz << -fy;
            if ( mAddNormals )
              mData << pNormal.x() << pNormal.z() << - pNormal.y();
          }
        }
      }
      catch ( ... )
      {
        QgsMessageLog::logMessage( "Triangulation failed. Skipping polygon...", "3D" );
      }

      delete cdt;
    }

    for ( int i = 0; i < polylinesToDelete.count(); ++i )
      qDeleteAll( polylinesToDelete[i] );
  }

  // add walls if extrusion is enabled
  if ( extrusionHeight != 0 )
  {
    _makeWalls( *exterior, false, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY );

    for ( int i = 0; i < polygon.numInteriorRings(); ++i )
      _makeWalls( *polygon.interiorRing( i ), true, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY );
  }
}
Beispiel #21
0
bool QgsGeometryAreaCheck::mergeWithNeighbor( const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const
{
  QgsFeaturePool *featurePool = mContext->featurePools[ layerId ];

  double maxVal = 0.;
  QgsFeature mergeFeature;
  int mergePartIdx = -1;
  bool matchFound = false;
  QgsGeometry featureGeometry = feature.geometry();
  const QgsAbstractGeometry *geom = featureGeometry.constGet();

  // Search for touching neighboring geometries
  const QgsFeatureIds intersects = featurePool->getIntersects( featureGeometry.boundingBox() );
  for ( QgsFeatureId testId : intersects )
  {
    QgsFeature testFeature;
    if ( !featurePool->get( testId, testFeature ) )
    {
      continue;
    }
    QgsGeometry testFeatureGeom = testFeature.geometry();
    const QgsAbstractGeometry *testGeom = testFeatureGeom.constGet();
    for ( int testPartIdx = 0, nTestParts = testGeom->partCount(); testPartIdx < nTestParts; ++testPartIdx )
    {
      if ( testId == feature.id() && testPartIdx == partIdx )
      {
        continue;
      }
      double len = QgsGeometryCheckerUtils::sharedEdgeLength( QgsGeometryCheckerUtils::getGeomPart( geom, partIdx ), QgsGeometryCheckerUtils::getGeomPart( testGeom, testPartIdx ), mContext->reducedTolerance );
      if ( len > 0. )
      {
        if ( method == MergeLongestEdge || method == MergeLargestArea )
        {
          double val;
          if ( method == MergeLongestEdge )
          {
            val = len;
          }
          else
          {
            if ( dynamic_cast<const QgsGeometryCollection *>( testGeom ) )
              val = static_cast<const QgsGeometryCollection *>( testGeom )->geometryN( testPartIdx )->area();
            else
              val = testGeom->area();
          }
          if ( val > maxVal )
          {
            maxVal = val;
            mergeFeature = testFeature;
            mergePartIdx = testPartIdx;
          }
        }
        else if ( method == MergeIdenticalAttribute )
        {
          if ( testFeature.attribute( mergeAttributeIndex ) == feature.attribute( mergeAttributeIndex ) )
          {
            mergeFeature = testFeature;
            mergePartIdx = testPartIdx;
            matchFound = true;
            break;
          }
        }
      }
    }
    if ( matchFound )
    {
      break;
    }
  }

  if ( !matchFound && maxVal == 0. )
  {
    return method == MergeIdenticalAttribute;
  }

  // Merge geometries
  QgsGeometry mergeFeatureGeom = mergeFeature.geometry();
  const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet();
  std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometryCheckerUtils::createGeomEngine( QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), mContext->reducedTolerance ) );
  QgsAbstractGeometry *combinedGeom = geomEngine->combine( QgsGeometryCheckerUtils::getGeomPart( geom, partIdx ), &errMsg );
  if ( !combinedGeom || combinedGeom->isEmpty() || !QgsWkbTypes::isSingleType( combinedGeom->wkbType() ) )
  {
    return false;
  }

  // Replace polygon in merge geometry
  if ( mergeFeature.id() == feature.id() && mergePartIdx > partIdx )
  {
    --mergePartIdx;
  }
  replaceFeatureGeometryPart( layerId, mergeFeature, mergePartIdx, combinedGeom, changes );
  // Remove polygon from source geometry
  deleteFeatureGeometryPart( layerId, feature, partIdx, changes );

  return true;
}
Beispiel #22
0
void QgsGeometryTypeCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
  QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ];
  QgsFeature feature;
  if ( !featurePool->get( error->featureId(), feature ) )
  {
    error->setObsolete();
    return;
  }
  QgsGeometry featureGeom = feature.geometry();
  const QgsAbstractGeometry *geom = featureGeom.constGet();

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

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

        feature.setGeometry( QgsGeometry( geomCollection ) );
        featurePool->updateFeature( feature );
        changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
      }
    }
    // Delete feature
    else
    {
      featurePool->deleteFeature( feature.id() );
      changes[error->layerId()][error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) );
    }
    error->setFixed( method );
  }
  else if ( method == Delete )
  {
    featurePool->deleteFeature( feature.id() );
    error->setFixed( method );
    changes[error->layerId()][error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) );
  }
  else
  {
    error->setFixFailed( tr( "Unknown method" ) );
  }
}
Beispiel #23
0
bool QgsGeometryGapCheck::mergeWithNeighbor( const QMap<QString, QgsFeaturePool *> &featurePools,
    QgsGeometryGapCheckError *err,
    Changes &changes, QString &errMsg ) const
{
  double maxVal = 0.;
  QString mergeLayerId;
  QgsFeature mergeFeature;
  int mergePartIdx = -1;

  const QgsGeometry geometry = err->geometry();
  const QgsAbstractGeometry *errGeometry = QgsGeometryCheckerUtils::getGeomPart( geometry.constGet(), 0 );

  const auto layerIds = err->neighbors().keys();
  // Search for touching neighboring geometries
  for ( const QString &layerId : layerIds )
  {
    QgsFeaturePool *featurePool = featurePools.value( layerId );
    std::unique_ptr<QgsAbstractGeometry> errLayerGeom( errGeometry->clone() );
    QgsCoordinateTransform ct( featurePool->crs(), mContext->mapCrs, mContext->transformContext );
    errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform );

    const auto featureIds = err->neighbors().value( layerId );

    for ( QgsFeatureId testId : featureIds )
    {
      QgsFeature testFeature;
      if ( !featurePool->getFeature( testId, testFeature ) )
      {
        continue;
      }
      QgsGeometry featureGeom = testFeature.geometry();
      const QgsAbstractGeometry *testGeom = featureGeom.constGet();
      for ( int iPart = 0, nParts = testGeom->partCount(); iPart < nParts; ++iPart )
      {
        double len = QgsGeometryCheckerUtils::sharedEdgeLength( errLayerGeom.get(), QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart ), mContext->reducedTolerance );
        if ( len > maxVal )
        {
          maxVal = len;
          mergeFeature = testFeature;
          mergePartIdx = iPart;
          mergeLayerId = layerId;
        }
      }
    }
  }

  if ( maxVal == 0. )
  {
    return false;
  }

  // Merge geometries
  QgsFeaturePool *featurePool = featurePools[ mergeLayerId ];
  std::unique_ptr<QgsAbstractGeometry> errLayerGeom( errGeometry->clone() );
  QgsCoordinateTransform ct( featurePool->crs(), mContext->mapCrs, mContext->transformContext );
  errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform );
  QgsGeometry mergeFeatureGeom = mergeFeature.geometry();
  const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet();
  std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( errLayerGeom.get(), mContext->reducedTolerance );
  std::unique_ptr<QgsAbstractGeometry> combinedGeom( geomEngine->combine( QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), &errMsg ) );
  if ( !combinedGeom || combinedGeom->isEmpty() || !QgsWkbTypes::isSingleType( combinedGeom->wkbType() ) )
  {
    return false;
  }

  // Add merged polygon to destination geometry
  replaceFeatureGeometryPart( featurePools, mergeLayerId, mergeFeature, mergePartIdx, combinedGeom.release(), changes );

  return true;
}
Beispiel #24
0
void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeight )
{
  const QgsCurve *exterior = polygon.exteriorRing();

  const QVector3D pNormal = _calculateNormal( exterior, mOriginX, mOriginY, mInvertNormals );
  const int pCount = exterior->numPoints();

  if ( pCount == 4 && polygon.numInteriorRings() == 0 )
  {
    // polygon is a triangle - write vertices to the output data array without triangulation
    QgsPoint pt;
    QgsVertexId::VertexType vt;
    for ( int i = 0; i < 3; i++ )
    {
      exterior->pointAt( i, pt, vt );
      mData << pt.x() - mOriginX << pt.z() << - pt.y() + mOriginY;
      if ( mAddNormals )
        mData << pNormal.x() << pNormal.z() << - pNormal.y();
    }

    if ( mAddBackFaces )
    {
      // the same triangle with reversed order of coordinates and inverted normal
      for ( int i = 2; i >= 0; i-- )
      {
        exterior->pointAt( i, pt, vt );
        mData << pt.x() - mOriginX << pt.z() << - pt.y() + mOriginY;
        if ( mAddNormals )
          mData << -pNormal.x() << -pNormal.z() << pNormal.y();
      }
    }
  }
  else
  {
    if ( !qgsDoubleNear( pNormal.length(), 1, 0.001 ) )
      return;  // this should not happen - pNormal should be normalized to unit length

    std::unique_ptr<QMatrix4x4> toNewBase, toOldBase;
    if ( pNormal != QVector3D( 0, 0, 1 ) )
    {
      // this is not a horizontal plane - need to reproject the polygon to a new base so that
      // we can do the triangulation in a plane

      QVector3D pXVector, pYVector;
      _normalVectorToXYVectors( pNormal, pXVector, pYVector );

      // so now we have three orthogonal unit vectors defining new base
      // let's build transform matrix. We actually need just a 3x3 matrix,
      // but Qt does not have good support for it, so using 4x4 matrix instead.
      toNewBase.reset( new QMatrix4x4(
                         pXVector.x(), pXVector.y(), pXVector.z(), 0,
                         pYVector.x(), pYVector.y(), pYVector.z(), 0,
                         pNormal.x(), pNormal.y(), pNormal.z(), 0,
                         0, 0, 0, 0 ) );

      // our 3x3 matrix is orthogonal, so for inverse we only need to transpose it
      toOldBase.reset( new QMatrix4x4( toNewBase->transposed() ) );
    }

    const QgsPoint ptStart( exterior->startPoint() );
    const QgsPoint pt0( QgsWkbTypes::PointZ, ptStart.x(), ptStart.y(), std::isnan( ptStart.z() ) ? 0 : ptStart.z() );

    // subtract ptFirst from geometry for better numerical stability in triangulation
    // and apply new 3D vector base if the polygon is not horizontal
    std::unique_ptr<QgsPolygon> polygonNew( _transform_polygon_to_new_base( polygon, pt0, toNewBase.get() ) );

    if ( _minimum_distance_between_coordinates( *polygonNew ) < 0.001 )
    {
      // when the distances between coordinates of input points are very small,
      // the triangulation likes to crash on numerical errors - when the distances are ~ 1e-5
      // Assuming that the coordinates should be in a projected CRS, we should be able
      // to simplify geometries that may cause problems and avoid possible crashes
      QgsGeometry polygonSimplified = QgsGeometry( polygonNew->clone() ).simplify( 0.001 );
      const QgsPolygon *polygonSimplifiedData = qgsgeometry_cast<const QgsPolygon *>( polygonSimplified.constGet() );
      if ( _minimum_distance_between_coordinates( *polygonSimplifiedData ) < 0.001 )
      {
        // Failed to fix that. It could be a really tiny geometry... or maybe they gave us
        // geometry in unprojected lat/lon coordinates
        QgsMessageLog::logMessage( QObject::tr( "geometry's coordinates are too close to each other and simplification failed - skipping" ), QObject::tr( "3D" ) );
        return;
      }
      else
      {
        polygonNew.reset( polygonSimplifiedData->clone() );
      }
    }

    if ( !_check_intersecting_rings( *polygonNew.get() ) )
    {
      // skip the polygon - it would cause a crash inside poly2tri library
      QgsMessageLog::logMessage( QObject::tr( "polygon rings self-intersect or intersect each other - skipping" ), QObject::tr( "3D" ) );
      return;
    }

    QList< std::vector<p2t::Point *> > polylinesToDelete;
    QHash<p2t::Point *, float> z;

    // polygon exterior
    std::vector<p2t::Point *> polyline;
    _ringToPoly2tri( polygonNew->exteriorRing(), polyline, z );
    polylinesToDelete << polyline;

    std::unique_ptr<p2t::CDT> cdt( new p2t::CDT( polyline ) );

    // polygon holes
    for ( int i = 0; i < polygonNew->numInteriorRings(); ++i )
    {
      std::vector<p2t::Point *> holePolyline;
      const QgsCurve *hole = polygonNew->interiorRing( i );

      _ringToPoly2tri( hole, holePolyline, z );

      cdt->AddHole( holePolyline );
      polylinesToDelete << holePolyline;
    }

    // run triangulation and write vertices to the output data array
    try
    {
      cdt->Triangulate();

      std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();

      for ( size_t i = 0; i < triangles.size(); ++i )
      {
        p2t::Triangle *t = triangles[i];
        for ( int j = 0; j < 3; ++j )
        {
          p2t::Point *p = t->GetPoint( j );
          QVector4D pt( p->x, p->y, z[p], 0 );
          if ( toOldBase )
            pt = *toOldBase * pt;
          const double fx = pt.x() - mOriginX + pt0.x();
          const double fy = pt.y() - mOriginY + pt0.y();
          const double fz = pt.z() + extrusionHeight + pt0.z();
          mData << fx << fz << -fy;
          if ( mAddNormals )
            mData << pNormal.x() << pNormal.z() << - pNormal.y();
        }

        if ( mAddBackFaces )
        {
          // the same triangle with reversed order of coordinates and inverted normal
          for ( int j = 2; j >= 0; --j )
          {
            p2t::Point *p = t->GetPoint( j );
            QVector4D pt( p->x, p->y, z[p], 0 );
            if ( toOldBase )
              pt = *toOldBase * pt;
            const double fx = pt.x() - mOriginX + pt0.x();
            const double fy = pt.y() - mOriginY + pt0.y();
            const double fz = pt.z() + extrusionHeight + pt0.z();
            mData << fx << fz << -fy;
            if ( mAddNormals )
              mData << -pNormal.x() << -pNormal.z() << pNormal.y();
          }
        }
      }
    }
    catch ( ... )
    {
      QgsMessageLog::logMessage( QObject::tr( "Triangulation failed. Skipping polygon…" ), QObject::tr( "3D" ) );
    }

    for ( int i = 0; i < polylinesToDelete.count(); ++i )
      qDeleteAll( polylinesToDelete[i] );
  }

  // add walls if extrusion is enabled
  if ( extrusionHeight != 0 )
  {
    _makeWalls( *exterior, false, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY );

    for ( int i = 0; i < polygon.numInteriorRings(); ++i )
      _makeWalls( *polygon.interiorRing( i ), true, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY );
  }
}
Beispiel #25
0
void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
  if ( feedback )
    feedback->setProgress( feedback->progress() + 1.0 );

  QVector<QgsGeometry> geomList;


  QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
  const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true );
  for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
  {
    geomList.append( layerFeature.geometry() );

    if ( feedback && feedback->isCanceled() )
    {
      geomList.clear();
      break;
    }
  }

  if ( geomList.isEmpty() )
  {
    return;
  }

  std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( nullptr, mContext->tolerance );
  geomEngine->prepareGeometry();

  // Create union of geometry
  QString errMsg;
  std::unique_ptr<QgsAbstractGeometry> unionGeom( geomEngine->combine( geomList, &errMsg ) );
  if ( !unionGeom )
  {
    messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
    return;
  }

  // Get envelope of union
  geomEngine = QgsGeometryCheckerUtils::createGeomEngine( unionGeom.get(), mContext->tolerance );
  geomEngine->prepareGeometry();
  std::unique_ptr<QgsAbstractGeometry> envelope( geomEngine->envelope( &errMsg ) );
  if ( !envelope )
  {
    messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
    return;
  }

  // Buffer envelope
  geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope.get(), mContext->tolerance );
  geomEngine->prepareGeometry();
  QgsAbstractGeometry *bufEnvelope = geomEngine->buffer( 2, 0, GEOSBUF_CAP_SQUARE, GEOSBUF_JOIN_MITRE, 4. );  //#spellok  //#spellok
  envelope.reset( bufEnvelope );

  // Compute difference between envelope and union to obtain gap polygons
  geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope.get(), mContext->tolerance );
  geomEngine->prepareGeometry();
  std::unique_ptr<QgsAbstractGeometry> diffGeom( geomEngine->difference( unionGeom.get(), &errMsg ) );
  if ( !diffGeom )
  {
    messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
    return;
  }

  // For each gap polygon which does not lie on the boundary, get neighboring polygons and add error
  for ( int iPart = 0, nParts = diffGeom->partCount(); iPart < nParts; ++iPart )
  {
    std::unique_ptr<QgsAbstractGeometry> gapGeom( QgsGeometryCheckerUtils::getGeomPart( diffGeom.get(), iPart )->clone() );
    // Skip the gap between features and boundingbox
    const double spacing = context()->tolerance;
    if ( gapGeom->boundingBox().snappedToGrid( spacing ) == envelope->boundingBox().snappedToGrid( spacing ) )
    {
      continue;
    }

    // Skip gaps above threshold
    if ( ( mGapThresholdMapUnits > 0 && gapGeom->area() > mGapThresholdMapUnits ) || gapGeom->area() < mContext->reducedTolerance )
    {
      continue;
    }

    QgsRectangle gapAreaBBox = gapGeom->boundingBox();

    // Get neighboring polygons
    QMap<QString, QgsFeatureIds> neighboringIds;
    const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds.keys(), gapAreaBBox, compatibleGeometryTypes(), mContext );
    for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
    {
      const QgsGeometry geom = layerFeature.geometry();
      if ( QgsGeometryCheckerUtils::sharedEdgeLength( gapGeom.get(), geom.constGet(), mContext->reducedTolerance ) > 0 )
      {
        neighboringIds[layerFeature.layer()->id()].insert( layerFeature.feature().id() );
        gapAreaBBox.combineExtentWith( layerFeature.geometry().boundingBox() );
      }
    }

    if ( neighboringIds.isEmpty() )
    {
      continue;
    }

    // Add error
    double area = gapGeom->area();
    errors.append( new QgsGeometryGapCheckError( this, QString(), QgsGeometry( gapGeom.release() ), neighboringIds, area, gapAreaBBox ) );
  }
}
Beispiel #26
0
QVariantMap QgsTransectAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
  Side orientation = static_cast< QgsTransectAlgorithm::Side >( parameterAsInt( parameters, QStringLiteral( "SIDE" ), context ) );
  double angle = fabs( parameterAsDouble( parameters, QStringLiteral( "ANGLE" ), context ) );
  bool dynamicAngle = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "ANGLE" ) );
  QgsProperty angleProperty;
  if ( dynamicAngle )
    angleProperty = parameters.value( QStringLiteral( "ANGLE" ) ).value< QgsProperty >();

  double length = parameterAsDouble( parameters, QStringLiteral( "LENGTH" ), context );
  bool dynamicLength = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "LENGTH" ) );
  QgsProperty lengthProperty;
  if ( dynamicLength )
    lengthProperty = parameters.value( QStringLiteral( "LENGTH" ) ).value< QgsProperty >();

  if ( orientation == QgsTransectAlgorithm::Both )
    length /= 2.0;

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

  QgsExpressionContext expressionContext = createExpressionContext( parameters, context, dynamic_cast< QgsProcessingFeatureSource * >( source.get() ) );

  QgsFields fields = source->fields();

  fields.append( QgsField( QStringLiteral( "TR_FID" ), QVariant::Int, QString(), 20 ) );
  fields.append( QgsField( QStringLiteral( "TR_ID" ), QVariant::Int, QString(), 20 ) );
  fields.append( QgsField( QStringLiteral( "TR_SEGMENT" ), QVariant::Int, QString(), 20 ) );
  fields.append( QgsField( QStringLiteral( "TR_ANGLE" ), QVariant::Double, QString(), 5, 2 ) );
  fields.append( QgsField( QStringLiteral( "TR_LENGTH" ), QVariant::Double, QString(), 20, 6 ) );
  fields.append( QgsField( QStringLiteral( "TR_ORIENT" ), QVariant::Int, QString(), 1 ) );

  QgsWkbTypes::Type outputWkb = QgsWkbTypes::LineString;
  if ( QgsWkbTypes::hasZ( source->wkbType() ) )
    outputWkb = QgsWkbTypes::addZ( outputWkb );
  if ( QgsWkbTypes::hasM( source->wkbType() ) )
    outputWkb = QgsWkbTypes::addM( outputWkb );

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

  QgsFeatureIterator features = source->getFeatures( );

  int current = -1;
  int number = 0;
  double step =  source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
  QgsFeature feat;


  while ( features.nextFeature( feat ) )
  {
    current++;
    if ( feedback->isCanceled() )
    {
      break;
    }

    feedback->setProgress( current * step );
    if ( !feat.hasGeometry() )
      continue;

    QgsGeometry inputGeometry = feat.geometry();

    if ( dynamicLength || dynamicAngle )
    {
      expressionContext.setFeature( feat );
    }

    double evaluatedLength = length;
    if ( dynamicLength )
      evaluatedLength = lengthProperty.valueAsDouble( context.expressionContext(), length );
    double evaluatedAngle = angle;
    if ( dynamicAngle )
      evaluatedAngle = angleProperty.valueAsDouble( context.expressionContext(), angle );

    inputGeometry.convertToMultiType();
    const QgsMultiLineString *multiLine = static_cast< const QgsMultiLineString *  >( inputGeometry.constGet() );
    for ( int id = 0; id < multiLine->numGeometries(); ++id )
    {
      const QgsLineString *line = static_cast< const QgsLineString * >( multiLine->geometryN( id ) );
      QgsAbstractGeometry::vertex_iterator it = line->vertices_begin();
      while ( it != line->vertices_end() )
      {
        QgsVertexId vertexId = it.vertexId();
        int i = vertexId.vertex;
        QgsFeature outFeat;
        QgsAttributes attrs = feat.attributes();
        attrs << current << number << i + 1 << evaluatedAngle <<
              ( ( orientation == QgsTransectAlgorithm::Both ) ? evaluatedLength * 2 : evaluatedLength ) <<
              orientation;
        outFeat.setAttributes( attrs );
        double angleAtVertex = line->vertexAngle( vertexId );
        outFeat.setGeometry( calcTransect( *it, angleAtVertex, evaluatedLength, orientation, evaluatedAngle ) );
        sink->addFeature( outFeat, QgsFeatureSink::FastInsert );
        number++;
        it++;
      }
    }
  }

  QVariantMap outputs;
  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
  return outputs;
}
Beispiel #27
0
QVariantMap QgsClipAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
  std::unique_ptr< QgsFeatureSource > featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
  if ( !featureSource )
    throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );

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

  QString dest;
  QgsWkbTypes::GeometryType sinkType = QgsWkbTypes::geometryType( featureSource->wkbType() );
  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, featureSource->fields(), QgsWkbTypes::multiType( featureSource->wkbType() ), featureSource->sourceCrs() ) );

  if ( !sink )
    throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );

  // first build up a list of clip geometries
  QVector< QgsGeometry > clipGeoms;
  QgsFeatureIterator it = maskSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QList< int >() ).setDestinationCrs( featureSource->sourceCrs(), context.transformContext() ) );
  QgsFeature f;
  while ( it.nextFeature( f ) )
  {
    if ( f.hasGeometry() )
      clipGeoms << f.geometry();
  }

  QVariantMap outputs;
  outputs.insert( QStringLiteral( "OUTPUT" ), dest );

  if ( clipGeoms.isEmpty() )
    return outputs;

  // are we clipping against a single feature? if so, we can show finer progress reports
  bool singleClipFeature = false;
  QgsGeometry combinedClipGeom;
  if ( clipGeoms.length() > 1 )
  {
    combinedClipGeom = QgsGeometry::unaryUnion( clipGeoms );
    if ( combinedClipGeom.isEmpty() )
    {
      throw QgsProcessingException( QObject::tr( "Could not create the combined clip geometry: %1" ).arg( combinedClipGeom.lastError() ) );
    }
    singleClipFeature = false;
  }
  else
  {
    combinedClipGeom = clipGeoms.at( 0 );
    singleClipFeature = true;
  }

  // use prepared geometries for faster intersection tests
  std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( combinedClipGeom.constGet() ) );
  engine->prepareGeometry();

  QgsFeatureIds testedFeatureIds;

  int i = -1;
  Q_FOREACH ( const QgsGeometry &clipGeom, clipGeoms )
  {
    i++;
    if ( feedback->isCanceled() )
    {
      break;
    }
    QgsFeatureIterator inputIt = featureSource->getFeatures( QgsFeatureRequest().setFilterRect( clipGeom.boundingBox() ) );
    QgsFeatureList inputFeatures;
    QgsFeature f;
    while ( inputIt.nextFeature( f ) )
      inputFeatures << f;

    if ( inputFeatures.isEmpty() )
      continue;

    double step = 0;
    if ( singleClipFeature )
      step = 100.0 / inputFeatures.length();

    int current = 0;
    Q_FOREACH ( const QgsFeature &inputFeature, inputFeatures )
    {
      if ( feedback->isCanceled() )
      {
        break;
      }

      if ( !inputFeature.hasGeometry() )
        continue;

      if ( testedFeatureIds.contains( inputFeature.id() ) )
      {
        // don't retest a feature we have already checked
        continue;
      }
      testedFeatureIds.insert( inputFeature.id() );

      if ( !engine->intersects( inputFeature.geometry().constGet() ) )
        continue;

      QgsGeometry newGeometry;
      if ( !engine->contains( inputFeature.geometry().constGet() ) )
      {
        QgsGeometry currentGeometry = inputFeature.geometry();
        newGeometry = combinedClipGeom.intersection( currentGeometry );
        if ( newGeometry.wkbType() == QgsWkbTypes::Unknown || QgsWkbTypes::flatType( newGeometry.wkbType() ) == QgsWkbTypes::GeometryCollection )
        {
          QgsGeometry intCom = inputFeature.geometry().combine( newGeometry );
          QgsGeometry intSym = inputFeature.geometry().symDifference( newGeometry );
          newGeometry = intCom.difference( intSym );
        }
      }
      else
      {
        // clip geometry totally contains feature geometry, so no need to perform intersection
        newGeometry = inputFeature.geometry();
      }

      if ( !QgsOverlayUtils::sanitizeIntersectionResult( newGeometry, sinkType ) )
        continue;

      QgsFeature outputFeature;
      outputFeature.setGeometry( newGeometry );
      outputFeature.setAttributes( inputFeature.attributes() );
      sink->addFeature( outputFeature, QgsFeatureSink::FastInsert );


      if ( singleClipFeature )
        feedback->setProgress( current * step );
    }

    if ( !singleClipFeature )
    {
      // coarse progress report for multiple clip geometries
      feedback->setProgress( 100.0 * static_cast< double >( i ) / clipGeoms.length() );
    }
  }
Beispiel #28
0
QString QgsExpression::formatPreviewString( const QVariant &value, const bool htmlOutput )
{
  static const int MAX_PREVIEW = 60;

  const QString startToken = htmlOutput ? QStringLiteral( "<i>&lt;" ) : QStringLiteral( "<" );
  const QString endToken = htmlOutput ? QStringLiteral( "&gt;</i>" ) : QStringLiteral( ">" );

  if ( value.canConvert<QgsGeometry>() )
  {
    //result is a geometry
    QgsGeometry geom = value.value<QgsGeometry>();
    if ( geom.isNull() )
      return startToken + tr( "empty geometry" ) + endToken;
    else
      return startToken + tr( "geometry: %1" ).arg( QgsWkbTypes::displayString( geom.constGet()->wkbType() ) )
             + endToken;
  }
  else if ( value.value< QgsWeakMapLayerPointer >().data() )
  {
    return startToken + tr( "map layer" ) + endToken;
  }
  else if ( !value.isValid() )
  {
    return htmlOutput ? tr( "<i>NULL</i>" ) : QString();
  }
  else if ( value.canConvert< QgsFeature >() )
  {
    //result is a feature
    QgsFeature feat = value.value<QgsFeature>();
    return startToken + tr( "feature: %1" ).arg( feat.id() ) + endToken;
  }
  else if ( value.canConvert< QgsInterval >() )
  {
    //result is a feature
    QgsInterval interval = value.value<QgsInterval>();
    return startToken + tr( "interval: %1 days" ).arg( interval.days() ) + endToken;
  }
  else if ( value.canConvert< QgsGradientColorRamp >() )
  {
    return startToken + tr( "gradient ramp" ) + endToken;
  }
  else if ( value.type() == QVariant::Date )
  {
    QDate dt = value.toDate();
    return startToken + tr( "date: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd" ) ) ) + endToken;
  }
  else if ( value.type() == QVariant::Time )
  {
    QTime tm = value.toTime();
    return startToken + tr( "time: %1" ).arg( tm.toString( QStringLiteral( "hh:mm:ss" ) ) ) + endToken;
  }
  else if ( value.type() == QVariant::DateTime )
  {
    QDateTime dt = value.toDateTime();
    return startToken + tr( "datetime: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd hh:mm:ss" ) ) ) + endToken;
  }
  else if ( value.type() == QVariant::String )
  {
    const QString previewString = value.toString();
    if ( previewString.length() > MAX_PREVIEW + 3 )
    {
      return tr( "'%1…'" ).arg( previewString.left( MAX_PREVIEW ) );
    }
    else
    {
      return '\'' + previewString + '\'';
    }
  }
  else if ( value.type() == QVariant::Map )
  {
    QString mapStr = QStringLiteral( "{" );
    const QVariantMap map = value.toMap();
    QString separator;
    for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
    {
      mapStr.append( separator );
      if ( separator.isEmpty() )
        separator = QStringLiteral( "," );

      mapStr.append( QStringLiteral( " '%1': %2" ).arg( it.key(), formatPreviewString( it.value(), htmlOutput ) ) );
      if ( mapStr.length() > MAX_PREVIEW - 3 )
      {
        mapStr = tr( "%1…" ).arg( mapStr.left( MAX_PREVIEW - 2 ) );
        break;
      }
    }
    if ( !map.empty() )
      mapStr += QStringLiteral( " " );
    mapStr += QStringLiteral( "}" );
    return mapStr;
  }
  else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
  {
    QString listStr = QStringLiteral( "[" );
    const QVariantList list = value.toList();
    QString separator;
    for ( const QVariant &arrayValue : list )
    {
      listStr.append( separator );
      if ( separator.isEmpty() )
        separator = QStringLiteral( "," );

      listStr.append( " " );
      listStr.append( formatPreviewString( arrayValue, htmlOutput ) );
      if ( listStr.length() > MAX_PREVIEW - 3 )
      {
        listStr = QString( tr( "%1…" ) ).arg( listStr.left( MAX_PREVIEW - 2 ) );
        break;
      }
    }
    if ( !list.empty() )
      listStr += QStringLiteral( " " );
    listStr += QStringLiteral( "]" );
    return listStr;
  }
  else
  {
    return value.toString();
  }
}
Beispiel #29
0
Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs3DMapSettings &map, const QgsPolygon3DSymbol &symbol, const QgsVectorLayer *layer, const QgsFeatureRequest &request )
{
  QgsPointXY origin( map.origin().x(), map.origin().y() );
  QList<QgsPolygon *> polygons;
  QList<float> extrusionHeightPerPolygon;  // will stay empty if not needed per polygon

  QgsExpressionContext ctx( _expressionContext3D() );
  ctx.setFields( layer->fields() );

  const QgsPropertyCollection &ddp = symbol.dataDefinedProperties();
  bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::PropertyHeight );
  bool hasDDExtrusion = ddp.isActive( QgsAbstract3DSymbol::PropertyExtrusionHeight );

  QgsFeature f;
  QgsFeatureIterator fi = layer->getFeatures( request );
  while ( fi.nextFeature( f ) )
  {
    if ( f.geometry().isNull() )
      continue;

    QgsGeometry geom = f.geometry();

    // segmentize curved geometries if necessary
    if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
      geom = QgsGeometry( geom.constGet()->segmentize() );

    const QgsAbstractGeometry *g = geom.constGet();

    ctx.setFeature( f );
    float height = symbol.height();
    float extrusionHeight = symbol.extrusionHeight();
    if ( hasDDHeight )
      height = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyHeight, ctx, height );
    if ( hasDDExtrusion )
      extrusionHeight = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyExtrusionHeight, ctx, extrusionHeight );

    if ( const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon *>( g ) )
    {
      QgsPolygon *polyClone = poly->clone();
      Qgs3DUtils::clampAltitudes( polyClone, symbol.altitudeClamping(), symbol.altitudeBinding(), height, map );
      polygons.append( polyClone );
      if ( hasDDExtrusion )
        extrusionHeightPerPolygon.append( extrusionHeight );
    }
    else if ( const QgsMultiPolygon *mpoly = qgsgeometry_cast< const QgsMultiPolygon *>( g ) )
    {
      for ( int i = 0; i < mpoly->numGeometries(); ++i )
      {
        const QgsAbstractGeometry *g2 = mpoly->geometryN( i );
        Q_ASSERT( QgsWkbTypes::flatType( g2->wkbType() ) == QgsWkbTypes::Polygon );
        QgsPolygon *polyClone = static_cast< const QgsPolygon *>( g2 )->clone();
        Qgs3DUtils::clampAltitudes( polyClone, symbol.altitudeClamping(), symbol.altitudeBinding(), height, map );
        polygons.append( polyClone );
        if ( hasDDExtrusion )
          extrusionHeightPerPolygon.append( extrusionHeight );
      }
    }
    else
      qDebug() << "not a polygon";
  }

  mGeometry = new QgsTessellatedPolygonGeometry;
  mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight(), extrusionHeightPerPolygon );

  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
  renderer->setGeometry( mGeometry );

  return renderer;
}
QgsVirtualLayerFeatureIterator::QgsVirtualLayerFeatureIterator( QgsVirtualLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request )
  : QgsAbstractFeatureIteratorFromSource<QgsVirtualLayerFeatureSource>( source, ownSource, request )
{

  // NOTE: this is really bad and should be removed.
  // it's only here to guard mSource->mSqlite - because if the provider is removed
  // then mSqlite will be meaningless.
  // this needs to be totally reworked so that mSqlite no longer depends on the provider
  // and can be fully encapsulated in the source
  if ( !mSource->mProvider )
  {
    close();
    return;
  }

  if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs )
  {
    mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs(), mRequest.transformContext() );
  }
  try
  {
    mFilterRect = filterRectToSourceCrs( mTransform );
  }
  catch ( QgsCsException & )
  {
    // can't reproject mFilterRect
    close();
    return;
  }

  try
  {
    QString tableName = mSource->mTableName;

    QStringList wheres;
    QString offset;
    QString subset = mSource->mSubset;
    if ( !subset.isEmpty() )
    {
      wheres << subset;
    }

    if ( !mSource->mDefinition.uid().isNull() )
    {
      // filters are only available when a column with unique id exists
      if ( mSource->mDefinition.hasDefinedGeometry() && !mFilterRect.isNull() )
      {
        bool do_exact = request.flags() & QgsFeatureRequest::ExactIntersect;
        QString mbr = QStringLiteral( "%1,%2,%3,%4" ).arg( mFilterRect.xMinimum() ).arg( mFilterRect.yMinimum() ).arg( mFilterRect.xMaximum() ).arg( mFilterRect.yMaximum() );
        wheres << quotedColumn( mSource->mDefinition.geometryField() ) + " is not null";
        wheres <<  QStringLiteral( "%1Intersects(%2,BuildMbr(%3))" )
               .arg( do_exact ? "" : "Mbr",
                     quotedColumn( mSource->mDefinition.geometryField() ),
                     mbr );
      }
      else if ( request.filterType() == QgsFeatureRequest::FilterFid )
      {
        wheres << QStringLiteral( "%1=%2" )
               .arg( quotedColumn( mSource->mDefinition.uid() ) )
               .arg( request.filterFid() );
      }
      else if ( request.filterType() == QgsFeatureRequest::FilterFids )
      {
        QString values = quotedColumn( mSource->mDefinition.uid() ) + " IN (";
        bool first = true;
        const auto constFilterFids = request.filterFids();
        for ( QgsFeatureId v : constFilterFids )
        {
          if ( !first )
          {
            values += QLatin1String( "," );
          }
          first = false;
          values += QString::number( v );
        }
        values += QLatin1String( ")" );
        wheres << values;
      }
    }
    else
    {
      if ( request.filterType() == QgsFeatureRequest::FilterFid )
      {
        if ( request.filterFid() >= 0 )
          offset = QStringLiteral( " LIMIT 1 OFFSET %1" ).arg( request.filterFid() );
        else // never return a feature if the id is negative
          offset = QStringLiteral( " LIMIT 0" );
      }
      else if ( !mFilterRect.isNull() &&
                mRequest.flags() & QgsFeatureRequest::ExactIntersect )
      {
        // if an exact intersection is requested, prepare the geometry to intersect
        QgsGeometry rectGeom = QgsGeometry::fromRect( mFilterRect );
        mRectEngine.reset( QgsGeometry::createGeometryEngine( rectGeom.constGet() ) );
        mRectEngine->prepareGeometry();
      }
    }

    if ( request.flags() & QgsFeatureRequest::SubsetOfAttributes )
    {
      // copy only selected fields
      const auto subsetOfAttributes = request.subsetOfAttributes();
      for ( int idx : subsetOfAttributes )
      {
        mAttributes << idx;
      }

      // ensure that all attributes required for expression filter are being fetched
      if ( request.filterType() == QgsFeatureRequest::FilterExpression )
      {
        const auto constReferencedColumns = request.filterExpression()->referencedColumns();
        for ( const QString &field : constReferencedColumns )
        {
          int attrIdx = mSource->mFields.lookupField( field );
          if ( !mAttributes.contains( attrIdx ) )
            mAttributes << attrIdx;
        }
      }

      // also need attributes required by order by
      if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mRequest.orderBy().isEmpty() )
      {
        const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields );
        for ( int attrIdx : usedAttributeIndices )
        {
          if ( !mAttributes.contains( attrIdx ) )
            mAttributes << attrIdx;
        }
      }
    }
    else
    {
      mAttributes = mSource->mFields.allAttributesList();
    }

    QString columns;
    {
      // the first column is always the uid (or 0)
      if ( !mSource->mDefinition.uid().isNull() )
      {
        columns = quotedColumn( mSource->mDefinition.uid() );
      }
      else
      {
        if ( request.filterType() == QgsFeatureRequest::FilterFid )
        {
          columns = QString::number( request.filterFid() );
        }
        else
        {
          columns = QStringLiteral( "0" );
        }
      }
      const auto constMAttributes = mAttributes;
      for ( int i : constMAttributes )
      {
        columns += QLatin1String( "," );
        QString cname = mSource->mFields.at( i ).name().toLower();
        columns += quotedColumn( cname );
      }
    }
    // the last column is the geometry, if any
    if ( ( !( request.flags() & QgsFeatureRequest::NoGeometry )
           || ( request.filterType() == QgsFeatureRequest::FilterExpression && request.filterExpression()->needsGeometry() ) )
         && !mSource->mDefinition.geometryField().isNull() && mSource->mDefinition.geometryField() != QLatin1String( "*no*" ) )
    {
      columns += "," + quotedColumn( mSource->mDefinition.geometryField() );
    }

    mSqlQuery = "SELECT " + columns + " FROM " + tableName;
    if ( !wheres.isEmpty() )
    {
      mSqlQuery += " WHERE " + wheres.join( QStringLiteral( " AND " ) );
    }

    if ( !offset.isEmpty() )
    {
      mSqlQuery += offset;
    }

    mQuery.reset( new Sqlite::Query( mSource->mSqlite, mSqlQuery ) );

    mFid = 0;
  }
  catch ( std::runtime_error &e )
  {
    QgsMessageLog::logMessage( e.what(), QObject::tr( "VLayer" ) );
    close();
  }
}