int QgsDemHeightMapGenerator::render( int x, int y, int z )
{
  Q_ASSERT( mJobs.isEmpty() );  // should be always just one active job...

  // extend the rect by half-pixel on each side? to get the values in "corners"
  QgsRectangle extent = mTilingScheme.tileToExtent( x, y, z );
  float mapUnitsPerPixel = extent.width() / mResolution;
  extent.grow( mapUnitsPerPixel / 2 );
  // but make sure not to go beyond the full extent (returns invalid values)
  QgsRectangle fullExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
  extent = extent.intersect( &fullExtent );

  JobData jd;
  jd.jobId = ++mLastJobId;
  jd.extent = extent;
  jd.timer.start();
  // make a clone of the data provider so it is safe to use in worker thread
  jd.future = QtConcurrent::run( _readDtmData, mClonedProvider, extent, mResolution );

  QFutureWatcher<QByteArray> *fw = new QFutureWatcher<QByteArray>;
  fw->setFuture( jd.future );
  connect( fw, &QFutureWatcher<QByteArray>::finished, this, &QgsDemHeightMapGenerator::onFutureFinished );

  mJobs.insert( fw, jd );

  return jd.jobId;
}
void QgsGeometryFollowBoundariesCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
  if ( !mIndex || !mCheckLayer )
  {
    return;
  }

  QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
  featureIds.remove( mCheckLayer->id() ); // Don't check layer against itself
  QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter );
  for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
  {
    const QgsAbstractGeometry *geom = layerFeature.geometry();

    // The geometry to crs of the check layer
    QgsCoordinateTransform crst = QgsCoordinateTransformCache::instance()->transform( layerFeature.layer().crs().authid(), mCheckLayer->crs().authid() );
    QScopedPointer<QgsAbstractGeometry> geomt( geom->clone() );
    geomt->transform( crst );

    QSharedPointer<QgsGeometryEngine> geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geomt.data(), mContext->tolerance );

    // Get potential reference features
    QgsRectangle searchBounds = geomt->boundingBox();
    searchBounds.grow( mContext->tolerance );
    QgsFeatureIds refFeatureIds = mIndex->intersects( searchBounds ).toSet();

    QgsFeatureRequest refFeatureRequest = QgsFeatureRequest().setFilterFids( refFeatureIds ).setSubsetOfAttributes( QgsAttributeList() );
    QgsFeatureIterator refFeatureIt = mCheckLayer->getFeatures( refFeatureRequest );

    if ( refFeatureIds.isEmpty() )
    {
      // If no potential reference features are found, the geometry is definitely not following boundaries of reference layer features
      errors.append( new QgsGeometryCheckError( this, layerFeature, QgsPointXY( geom->centroid() ) ) );
    }
    else
    {
      // All reference features must be either contained or disjoint from tested geometry
      QgsFeature refFeature;
      while ( refFeatureIt.nextFeature( refFeature ) )
      {
        QgsAbstractGeometry *refGeom = refFeature.geometry().geometry();
        QSharedPointer<QgsGeometryEngine> refgeomEngine = QgsGeometryCheckerUtils::createGeomEngine( refGeom, mContext->tolerance );
        QScopedPointer<QgsAbstractGeometry> reducedRefGeom( refgeomEngine->buffer( -mContext->tolerance, 0 ) );
        if ( !( geomEngine->contains( reducedRefGeom.data() ) || geomEngine->disjoint( reducedRefGeom.data() ) ) )
        {
          errors.append( new QgsGeometryCheckError( this, layerFeature, QgsPointXY( geom->centroid() ) ) );
          break;
        }
      }
    }
  }
}
QByteArray QgsDemHeightMapGenerator::renderSynchronously( int x, int y, int z )
{
  // extend the rect by half-pixel on each side? to get the values in "corners"
  QgsRectangle extent = mTilingScheme.tileToExtent( x, y, z );
  float mapUnitsPerPixel = extent.width() / mResolution;
  extent.grow( mapUnitsPerPixel / 2 );
  // but make sure not to go beyond the full extent (returns invalid values)
  QgsRectangle fullExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
  extent = extent.intersect( &fullExtent );

  QgsRasterBlock *block = mDtm->dataProvider()->block( 1, extent, mResolution, mResolution );

  QByteArray data;
  if ( block )
  {
    block->convert( Qgis::Float32 ); // currently we expect just floats
    data = block->data();
    data.detach();  // this should make a deep copy
    delete block;
  }

  return data;
}
Exemple #4
0
bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, bool triggerRepaint )
{
  mMessages.clear();
  if ( error->status() >= QgsGeometryCheckError::StatusFixed )
  {
    return true;
  }
#if 0
  QTextStream( stdout ) << "Fixing " << error->description() << ": " << error->layerId() << ":" << error->featureId() << " @[" << error->vidx().part << ", " << error->vidx().ring << ", " << error->vidx().vertex << "](" << error->location().x() << ", " << error->location().y() << ") = " << error->value().toString() << endl;
#endif

  QgsGeometryCheck::Changes changes;
  QgsRectangle recheckArea = error->affectedAreaBBox();

  error->check()->fixError( error, method, mMergeAttributeIndices, changes );
#if 0
  QTextStream( stdout ) << " * Status: " << error->resolutionMessage() << endl;
  static QVector<QString> strChangeWhat = { "ChangeFeature", "ChangePart", "ChangeRing", "ChangeNode" };
  static QVector<QString> strChangeType = { "ChangeAdded", "ChangeRemoved", "ChangeChanged" };
  for ( const QString &layerId : changes.keys() )
  {
    for ( const QgsFeatureId &fid : changes[layerId].keys() )
    {
      for ( const QgsGeometryCheck::Change &change : changes[layerId][fid] )
      {
        QTextStream( stdout ) << " * Change: " << layerId << ":" << fid << " :: " << strChangeWhat[change.what] << ":" << strChangeType[change.type] << ":(" << change.vidx.part << "," << change.vidx.ring << "," << change.vidx.vertex << ")" << endl;
      }
    }
  }
#endif
  emit errorUpdated( error, true );
  if ( error->status() != QgsGeometryCheckError::StatusFixed )
  {
    return false;
  }

  // If nothing was changed, stop here
  if ( changes.isEmpty() )
  {
    return true;
  }

  // Determine what to recheck
  // - Collect all features which were changed, get affected area
  QMap<QString, QSet<QgsFeatureId>> recheckFeatures;
  for ( auto it = changes.constBegin(); it != changes.constEnd(); ++it )
  {
    const QMap<QgsFeatureId, QList<QgsGeometryCheck::Change>> &layerChanges = it.value();
    QgsFeaturePool *featurePool = mContext->featurePools[it.key()];
    QgsCoordinateTransform t( featurePool->getLayer()->crs(), mContext->mapCrs, QgsProject::instance() );
    for ( auto layerChangeIt = layerChanges.constBegin(); layerChangeIt != layerChanges.constEnd(); ++layerChangeIt )
    {
      bool removed = false;
      for ( const QgsGeometryCheck::Change &change : layerChangeIt.value() )
      {
        if ( change.what == QgsGeometryCheck::ChangeFeature && change.type == QgsGeometryCheck::ChangeRemoved )
        {
          removed = true;
          break;
        }
      }
      if ( !removed )
      {
        QgsFeature f;
        if ( featurePool->get( layerChangeIt.key(), f ) )
        {
          recheckFeatures[it.key()].insert( layerChangeIt.key() );
          recheckArea.combineExtentWith( t.transformBoundingBox( f.geometry().boundingBox() ) );
        }
      }
    }
  }
  // - Determine extent to recheck for gaps
  for ( QgsGeometryCheckError *err : qgis::as_const( mCheckErrors ) )
  {
    if ( err->check()->getCheckType() == QgsGeometryCheck::LayerCheck )
    {
      if ( err->affectedAreaBBox().intersects( recheckArea ) )
      {
        recheckArea.combineExtentWith( err->affectedAreaBBox() );
      }
    }
  }
  recheckArea.grow( 10 * mContext->tolerance );
  QMap<QString, QgsFeatureIds> recheckAreaFeatures;
  for ( const QString &layerId : mContext->featurePools.keys() )
  {
    QgsFeaturePool *featurePool = mContext->featurePools[layerId];
    QgsCoordinateTransform t( mContext->mapCrs, featurePool->getLayer()->crs(), QgsProject::instance() );
    recheckAreaFeatures[layerId] = featurePool->getIntersects( t.transform( recheckArea ) );
  }

  // Recheck feature / changed area to detect new errors
  QList<QgsGeometryCheckError *> recheckErrors;
  for ( const QgsGeometryCheck *check : qgis::as_const( mChecks ) )
  {
    if ( check->getCheckType() == QgsGeometryCheck::LayerCheck )
    {
      if ( !recheckAreaFeatures.isEmpty() )
      {
        check->collectErrors( recheckErrors, mMessages, nullptr, recheckAreaFeatures );
      }
    }
    else
    {
      if ( !recheckFeatures.isEmpty() )
      {
        check->collectErrors( recheckErrors, mMessages, nullptr, recheckFeatures );
      }
    }
  }

  // Go through error list, update other errors of the checked feature
  for ( QgsGeometryCheckError *err : qgis::as_const( mCheckErrors ) )
  {
    if ( err == error || err->status() == QgsGeometryCheckError::StatusObsolete )
    {
      continue;
    }

    QgsGeometryCheckError::Status oldStatus = err->status();

    bool handled = err->handleChanges( changes );

    // Check if this error now matches one found when rechecking the feature/area
    QgsGeometryCheckError *matchErr = nullptr;
    int nMatch = 0;
    for ( QgsGeometryCheckError *recheckErr : qgis::as_const( recheckErrors ) )
    {
      if ( recheckErr->isEqual( err ) || recheckErr->closeMatch( err ) )
      {
        ++nMatch;
        matchErr = recheckErr;
      }
    }
    // If just one close match was found, take it
    if ( nMatch == 1 && matchErr )
    {
      err->update( matchErr );
      emit errorUpdated( err, err->status() != oldStatus );
      recheckErrors.removeAll( matchErr );
      delete matchErr;
      continue;
    }

    // If no match is found and the error is not fixed or obsolete, set it to obsolete if...
    if ( err->status() < QgsGeometryCheckError::StatusFixed &&
         (
           // changes weren't handled
           !handled ||
           // or if it is a FeatureNodeCheck or FeatureCheck error whose feature was rechecked
           ( err->check()->getCheckType() <= QgsGeometryCheck::FeatureCheck && recheckFeatures[err->layerId()].contains( err->featureId() ) ) ||
           // or if it is a LayerCheck error within the rechecked area
           ( err->check()->getCheckType() == QgsGeometryCheck::LayerCheck && recheckArea.contains( err->affectedAreaBBox() ) )
         )
       )
    {
      err->setObsolete();
      emit errorUpdated( err, err->status() != oldStatus );
    }
  }

  // Add new errors
  for ( QgsGeometryCheckError *recheckErr : qgis::as_const( recheckErrors ) )
  {
    emit errorAdded( recheckErr );
    mCheckErrors.append( recheckErr );
  }

  if ( triggerRepaint )
  {
    for ( const QString &layerId : changes.keys() )
    {
      mContext->featurePools[layerId]->getLayer()->triggerRepaint();
    }
  }

  return true;
}