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; }
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; }