void QgsMapToolOffsetCurve::applyOffset() { QgsVectorLayer* layer = currentVectorLayer(); if ( !layer ) { deleteRubberBandAndGeometry(); notifyNotVectorLayer(); return; } // no modification if ( !mGeometryModified ) { deleteRubberBandAndGeometry(); layer->destroyEditCommand(); deleteDistanceWidget(); return; } if ( mMultiPartGeometry ) { mModifiedGeometry.convertToMultiType(); } layer->beginEditCommand( tr( "Offset curve" ) ); bool editOk; if ( mSourceLayerId == layer->id() && !mForceCopy ) { editOk = layer->changeGeometry( mModifiedFeature, &mModifiedGeometry ); } else { QgsFeature f; f.setGeometry( mModifiedGeometry ); //add empty values for all fields (allows inserting attribute values via the feature form in the same session) QgsAttributes attrs( layer->fields().count() ); const QgsFields& fields = layer->fields(); for ( int idx = 0; idx < fields.count(); ++idx ) { attrs[idx] = QVariant(); } f.setAttributes( attrs ); editOk = layer->addFeature( f ); } if ( editOk ) { layer->endEditCommand(); } else { layer->destroyEditCommand(); } deleteRubberBandAndGeometry(); deleteDistanceWidget(); delete mSnapVertexMarker; mSnapVertexMarker = nullptr; mForceCopy = false; mCanvas->refresh(); }
void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent* e ) { if ( !mCanvas ) { return; } QgsVectorLayer* layer = currentVectorLayer(); if ( !layer ) { deleteRubberBandAndGeometry(); notifyNotVectorLayer(); return; } if ( e->button() == Qt::RightButton ) { deleteRubberBandAndGeometry(); deleteDistanceWidget(); return; } if ( !mOriginalGeometry ) { deleteRubberBandAndGeometry(); mGeometryModified = false; mForceCopy = false; if ( e->button() == Qt::RightButton ) { return; } QgsSnappingUtils* snapping = mCanvas->snappingUtils(); // store previous settings int oldType; double oldSearchRadius; QgsTolerance::UnitType oldSearchRadiusUnit; QgsSnappingUtils::SnapToMapMode oldMode = snapping->snapToMapMode(); snapping->defaultSettings( oldType, oldSearchRadius, oldSearchRadiusUnit ); // setup new settings (temporary) QSettings settings; snapping->setSnapToMapMode( QgsSnappingUtils::SnapAllLayers ); snapping->setDefaultSettings( QgsPointLocator::Edge, settings.value( "/qgis/digitizing/search_radius_vertex_edit", 10 ).toDouble(), ( QgsTolerance::UnitType ) settings.value( "/qgis/digitizing/search_radius_vertex_edit_unit", QgsTolerance::Pixels ).toInt() ); QgsPointLocator::Match match = snapping->snapToMap( e->pos() ); // restore old settings snapping->setSnapToMapMode( oldMode ); snapping->setDefaultSettings( oldType, oldSearchRadius, oldSearchRadiusUnit ); if ( match.hasEdge() && match.layer() ) { mSourceLayerId = match.layer()->id(); QgsFeature fet; if ( match.layer()->getFeatures( QgsFeatureRequest( match.featureId() ) ).nextFeature( fet ) ) { mForceCopy = ( e->modifiers() & Qt::ControlModifier ); //no geometry modification if ctrl is pressed mOriginalGeometry = createOriginGeometry( match.layer(), match, fet ); mRubberBand = createRubberBand(); if ( mRubberBand ) { mRubberBand->setToGeometry( mOriginalGeometry, layer ); } mModifiedFeature = fet.id(); createDistanceWidget(); } } return; } applyOffset(); }
/** * This slot is coonnected to the map canvas. When the canvas is done drawing the slot is fired to display thee highlighting symbol * @param thePainter - Pointer to the QPainter object */ void eVisGenericEventBrowserGui::renderSymbol( QPainter* thePainter ) { if ( !mFeatureIds.isEmpty() && mVectorLayer ) { //Get a pointer to the current feature QgsFeature* myFeature = featureAtId( mFeatureIds.at( mCurrentFeatureIndex ) ); if ( !myFeature ) return; QgsPoint myPoint = myFeature->geometry().asPoint(); myPoint = mCanvas->mapSettings().layerToMapCoordinates( mVectorLayer, myPoint ); mCanvas->getCoordinateTransform()->transform( &myPoint ); if ( mConfiguration.isDisplayCompassBearingSet() ) { //Make a copy of the pointersymbol and rotate it based on the values in the attribute field QPixmap myTempPixmap( mPointerSymbol.height(), mPointerSymbol.height() ); myTempPixmap.fill( QColor( 255, 255, 255, 0 ) ); QPainter p( &myTempPixmap ); QMatrix wm; wm.translate( myTempPixmap.width() / 2, myTempPixmap.height() / 2 ); // really center double myBearing = mCompassBearing; if ( mConfiguration.isManualCompassOffsetSet() ) { myBearing = mCompassBearing + mConfiguration.compassOffset(); } else { myBearing = mCompassBearing + mCompassOffset; } if ( myBearing < 0.0 ) { while ( myBearing < 0.0 ) myBearing = 360.0 + myBearing; } else if ( myBearing >= 360.0 ) { while ( myBearing >= 360.0 ) myBearing = myBearing - 360.0; } wm.rotate( myBearing ); p.setWorldMatrix( wm ); p.drawPixmap( -mPointerSymbol.width() / 2, -mPointerSymbol.height() / 2, mPointerSymbol ); int xShift = ( int )myPoint.x() - ( myTempPixmap.width() / 2 ); int yShift = ( int )myPoint.y() - ( myTempPixmap.height() / 2 ); thePainter->drawPixmap( xShift, yShift, myTempPixmap ); } else { int xShift = ( int )myPoint.x() - ( mHighlightSymbol.width() / 2 ); int yShift = ( int )myPoint.y() - ( mHighlightSymbol.height() / 2 ); thePainter->drawPixmap( xShift, yShift, mHighlightSymbol ); } } }
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const { QgsFeature feature; if ( !mFeaturePool->get( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsGeometry g = feature.geometry(); QgsAbstractGeometry *geometry = g.geometry(); QgsVertexId vidx = error->vidx(); // Check if point still exists if ( !vidx.isValid( geometry ) ) { error->setObsolete(); return; } // Check if error still applies int n = QgsGeometryCheckerUtils::polyLineSize( geometry, vidx.part, vidx.ring ); if ( n == 0 ) { error->setObsolete(); return; } const QgsPoint &p1 = geometry->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex - 1 + n ) % n ) ); const QgsPoint &p2 = geometry->vertexAt( vidx ); const QgsPoint &p3 = geometry->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ); QgsVector v21, v23; try { v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normalized(); v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normalized(); } catch ( const QgsException & ) { error->setObsolete(); return; } double angle = std::acos( v21 * v23 ) / M_PI * 180.0; if ( angle >= mMinAngle ) { error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == DeleteNode ) { if ( !QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) ) { error->setFixFailed( tr( "Resulting geometry is degenerate" ) ); } else if ( !geometry->deleteVertex( error->vidx() ) ) { error->setFixFailed( tr( "Failed to delete vertex" ) ); } else { changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) ); if ( QgsGeometryUtils::sqrDistance2D( p1, p3 ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance() && QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) && geometry->deleteVertex( error->vidx() ) ) // error->vidx points to p3 after removing p2 { changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ) ); } feature.setGeometry( g ); mFeaturePool->updateFeature( feature ); error->setFixed( method ); } } else { error->setFixFailed( tr( "Unknown method" ) ); } }
bool QgsGeometryAnalyzer::buffer( QgsVectorLayer* layer, const QString& shapefileName, double bufferDistance, bool onlySelectedFeatures, bool dissolve, int bufferDistanceField, QProgressDialog* p ) { if ( !layer ) { return false; } QgsVectorDataProvider* dp = layer->dataProvider(); if ( !dp ) { return false; } QGis::WkbType outputType = QGis::WKBPolygon; if ( dissolve ) { outputType = QGis::WKBMultiPolygon; } QgsCoordinateReferenceSystem crs = layer->crs(); QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, &crs ); QgsFeature currentFeature; QgsGeometry *dissolveGeometry = nullptr; //dissolve geometry (if dissolve enabled) //take only selection if ( onlySelectedFeatures ) { //use QgsVectorLayer::featureAtId const QgsFeatureIds selection = layer->selectedFeaturesIds(); if ( p ) { p->setMaximum( selection.size() ); } int processedFeatures = 0; QgsFeatureIds::const_iterator it = selection.constBegin(); for ( ; it != selection.constEnd(); ++it ) { if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { break; } if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) ) { continue; } bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, &dissolveGeometry, bufferDistance, bufferDistanceField ); ++processedFeatures; } if ( p ) { p->setValue( selection.size() ); } } //take all features else { QgsFeatureIterator fit = layer->getFeatures(); int featureCount = layer->featureCount(); if ( p ) { p->setMaximum( featureCount ); } int processedFeatures = 0; while ( fit.nextFeature( currentFeature ) ) { if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { break; } bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, &dissolveGeometry, bufferDistance, bufferDistanceField ); ++processedFeatures; } if ( p ) { p->setValue( featureCount ); } } if ( dissolve ) { QgsFeature dissolveFeature; if ( !dissolveGeometry ) { QgsDebugMsg( "no dissolved geometry - should not happen" ); return false; } dissolveFeature.setGeometry( dissolveGeometry ); vWriter.addFeature( dissolveFeature ); } return true; }
void QgsGeometryCheckerSetupTab::runChecks() { // Get selected layer const QList<QgsVectorLayer *> layers = getSelectedLayers(); if ( layers.isEmpty() ) return; if ( ui.radioButtonOutputNew->isChecked() ) { for ( QgsVectorLayer *layer : layers ) { if ( layer->dataProvider()->dataSourceUri().startsWith( ui.lineEditOutputDirectory->text() ) ) { QMessageBox::critical( this, tr( "Check Geometries" ), tr( "The chosen output directory contains one or more input layers." ) ); return; } } } QgsVectorLayer *lineLayerCheckLayer = ui.comboLineLayerIntersection->isEnabled() ? QgsProject::instance()->mapLayer<QgsVectorLayer *>( ui.comboLineLayerIntersection->currentData().toString() ) : nullptr; QgsVectorLayer *followBoundaryCheckLayer = ui.comboBoxFollowBoundaries->isEnabled() ? QgsProject::instance()->mapLayer<QgsVectorLayer *>( ui.comboBoxFollowBoundaries->currentData().toString() ) : nullptr; if ( layers.contains( lineLayerCheckLayer ) || layers.contains( followBoundaryCheckLayer ) ) { QMessageBox::critical( this, tr( "Check Geometries" ), tr( "The selected input layers cannot contain a layer also selected for a topology check." ) ); return; } for ( QgsVectorLayer *layer : layers ) { if ( layer->isEditable() ) { QMessageBox::critical( this, tr( "Check Geometries" ), tr( "Input layer '%1' is not allowed to be in editing mode." ).arg( layer->name() ) ); return; } } bool selectedOnly = ui.checkBoxInputSelectedOnly->isChecked(); // Set window busy setCursor( Qt::WaitCursor ); mRunButton->setEnabled( false ); ui.labelStatus->setText( tr( "<b>Preparing output…</b>" ) ); ui.labelStatus->show(); QApplication::processEvents( QEventLoop::ExcludeUserInputEvents ); QList<QgsVectorLayer *> processLayers; if ( ui.radioButtonOutputNew->isChecked() ) { // Get output directory and file extension QDir outputDir = QDir( ui.lineEditOutputDirectory->text() ); QString outputDriverName = ui.comboBoxOutputFormat->currentData().toString(); QgsVectorFileWriter::MetaData metadata; if ( !QgsVectorFileWriter::driverMetadata( outputDriverName, metadata ) ) { QMessageBox::critical( this, tr( "Check Geometries" ), tr( "The specified output format cannot be recognized." ) ); mRunButton->setEnabled( true ); ui.labelStatus->hide(); unsetCursor(); return; } QString outputExtension = metadata.ext; // List over input layers, check which existing project layers need to be removed and create output layers QString filenamePrefix = ui.lineEditFilenamePrefix->text(); QSettings().setValue( "/geometry_checker/previous_values/filename_prefix", filenamePrefix ); QStringList toRemove; QStringList createErrors; for ( QgsVectorLayer *layer : layers ) { QString outputPath = outputDir.absoluteFilePath( filenamePrefix + layer->name() + "." + outputExtension ); // Remove existing layer with same uri from project for ( QgsVectorLayer *projectLayer : QgsProject::instance()->layers<QgsVectorLayer *>() ) { if ( projectLayer->dataProvider()->dataSourceUri().startsWith( outputPath ) ) { toRemove.append( projectLayer->id() ); } } // Create output layer QString errMsg; QgsVectorFileWriter::WriterError err = QgsVectorFileWriter::writeAsVectorFormat( layer, outputPath, layer->dataProvider()->encoding(), layer->crs(), outputDriverName, selectedOnly, &errMsg ); if ( err != QgsVectorFileWriter::NoError ) { createErrors.append( errMsg ); continue; } const QgsVectorLayer::LayerOptions options { QgsProject::instance()->transformContext() }; QgsVectorLayer *newlayer = new QgsVectorLayer( outputPath, QFileInfo( outputPath ).completeBaseName(), QStringLiteral( "ogr" ), options ); if ( selectedOnly ) { QgsFeature feature; // Get features to select (only selected features were written up to this point) QgsFeatureIds selectedFeatures = newlayer->allFeatureIds(); // Write non-selected feature ids QgsFeatureList features; QgsFeatureIterator it = layer->getFeatures(); while ( it.nextFeature( feature ) ) { if ( !layer->selectedFeatureIds().contains( feature.id() ) ) { features.append( feature ); } } newlayer->dataProvider()->addFeatures( features ); // Set selected features newlayer->selectByIds( selectedFeatures ); } processLayers.append( newlayer ); } // Remove layers from project if ( !toRemove.isEmpty() ) { QgsProject::instance()->removeMapLayers( toRemove ); } // Error if an output layer could not be created if ( !createErrors.isEmpty() ) { QMessageBox::critical( this, tr( "Check Geometries" ), tr( "Failed to create one or more output layers:\n%1" ).arg( createErrors.join( "\n" ) ) ); mRunButton->setEnabled( true ); ui.labelStatus->hide(); unsetCursor(); return; } } else { processLayers = layers; } // Check if output layers are editable QList<QgsVectorLayer *> nonEditableLayers; for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) ) { if ( ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeGeometries ) == 0 ) { nonEditableLayers.append( layer ); } } if ( !nonEditableLayers.isEmpty() ) { QStringList nonEditableLayerNames; for ( QgsVectorLayer *layer : nonEditableLayers ) { nonEditableLayerNames.append( layer->name() ); } if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Check Geometries" ), tr( "The following output layers are in a format that does not support editing features:\n%1\n\nThe geometry check can be performed, but it will not be possible to fix any errors. Do you want to continue?" ).arg( nonEditableLayerNames.join( "\n" ) ), QMessageBox::Yes, QMessageBox::No ) ) { if ( ui.radioButtonOutputNew->isChecked() ) { for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) ) { QString layerPath = layer->dataProvider()->dataSourceUri(); delete layer; if ( ui.comboBoxOutputFormat->currentText() == QLatin1String( "ESRI Shapefile" ) ) { QgsVectorFileWriter::deleteShapeFile( layerPath ); } else { QFile( layerPath ).remove(); } } mRunButton->setEnabled( true ); ui.labelStatus->hide(); unsetCursor(); } return; } } // Setup checker ui.labelStatus->setText( tr( "<b>Building spatial index…</b>" ) ); QApplication::processEvents( QEventLoop::ExcludeUserInputEvents ); QMap<QString, QgsFeaturePool *> featurePools; for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) ) { featurePools.insert( layer->id(), new QgsVectorDataProviderFeaturePool( layer, selectedOnly ) ); } // LineLayerIntersection check is enabled, make sure there is also a feature pool for that layer if ( ui.checkLineLayerIntersection->isChecked() && !featurePools.keys().contains( ui.comboLineLayerIntersection->currentData().toString() ) ) { QgsVectorLayer *layer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( ui.comboLineLayerIntersection->currentData().toString() ); Q_ASSERT( layer ); featurePools.insert( layer->id(), new QgsVectorDataProviderFeaturePool( layer, selectedOnly ) ); } QgsGeometryCheckContext *context = new QgsGeometryCheckContext( ui.spinBoxTolerance->value(), QgsProject::instance()->crs(), QgsProject::instance()->transformContext() ); QList<QgsGeometryCheck *> checks; for ( const QgsGeometryCheckFactory *factory : QgsGeometryCheckFactoryRegistry::getCheckFactories() ) { QgsGeometryCheck *check = factory->createInstance( context, ui ); if ( check ) { checks.append( check ); } } QgsGeometryChecker *checker = new QgsGeometryChecker( checks, context, featurePools ); emit checkerStarted( checker ); if ( ui.radioButtonOutputNew->isChecked() ) { QList<QgsMapLayer *> addLayers; for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) ) { addLayers.append( layer ); } QgsProject::instance()->addMapLayers( addLayers ); } // Run ui.buttonBox->addButton( mAbortButton, QDialogButtonBox::ActionRole ); mRunButton->hide(); ui.progressBar->setRange( 0, 0 ); ui.labelStatus->hide(); ui.progressBar->show(); ui.widgetInputs->setEnabled( false ); QEventLoop evLoop; QFutureWatcher<void> futureWatcher; connect( checker, &QgsGeometryChecker::progressValue, ui.progressBar, &QProgressBar::setValue ); connect( &futureWatcher, &QFutureWatcherBase::finished, &evLoop, &QEventLoop::quit ); connect( mAbortButton, &QAbstractButton::clicked, &futureWatcher, &QFutureWatcherBase::cancel ); connect( mAbortButton, &QAbstractButton::clicked, this, &QgsGeometryCheckerSetupTab::showCancelFeedback ); mIsRunningInBackground = true; int maxSteps = 0; futureWatcher.setFuture( checker->execute( &maxSteps ) ); ui.progressBar->setRange( 0, maxSteps ); evLoop.exec(); mIsRunningInBackground = false; // Restore window unsetCursor(); mAbortButton->setEnabled( true ); ui.buttonBox->removeButton( mAbortButton ); mRunButton->setEnabled( true ); mRunButton->show(); ui.progressBar->hide(); ui.labelStatus->hide(); ui.widgetInputs->setEnabled( true ); // Show result emit checkerFinished( !futureWatcher.isCanceled() ); }
QgsGraduatedSymbolRendererV2* QgsGraduatedSymbolRendererV2::createRenderer( QgsVectorLayer* vlayer, QString attrName, int classes, Mode mode, QgsSymbolV2* symbol, QgsVectorColorRampV2* ramp ) { if ( classes < 1 ) return NULL; int attrNum = vlayer->fieldNameIndex( attrName ); double minimum = vlayer->minimumValue( attrNum ).toDouble(); double maximum = vlayer->maximumValue( attrNum ).toDouble(); QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) ); QList<double> breaks; QList<int> labels; if ( mode == EqualInterval ) { breaks = _calcEqualIntervalBreaks( minimum, maximum, classes ); } else if ( mode == Pretty ) { breaks = _calcPrettyBreaks( minimum, maximum, classes ); } else if ( mode == Quantile || mode == Jenks || mode == StdDev ) { // get values from layer QList<double> values; QgsFeature f; QgsAttributeList lst; lst.append( attrNum ); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( lst ) ); // create list of non-null attribute values while ( fit.nextFeature( f ) ) if ( !f.attribute( attrNum ).isNull() ) values.append( f.attribute( attrNum ).toDouble() ); // calculate the breaks if ( mode == Quantile ) { breaks = _calcQuantileBreaks( values, classes ); } else if ( mode == Jenks ) { breaks = _calcJenksBreaks( values, classes, minimum, maximum ); } else if ( mode == StdDev ) { breaks = _calcStdDevBreaks( values, classes, labels ); } } else { Q_ASSERT( false ); } QgsRangeList ranges; double lower, upper = minimum; QString label; // "breaks" list contains all values at class breaks plus maximum as last break int i = 0; for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i ) { lower = upper; // upper border from last interval upper = *it; if ( mode == StdDev ) { if ( i == 0 ) { label = "< " + QString::number( labels[i], 'i', 0 ) + " Std Dev"; } else if ( i == labels.count() - 1 ) { label = ">= " + QString::number( labels[i-1], 'i', 0 ) + " Std Dev"; } else { label = QString::number( labels[i-1], 'i', 0 ) + " Std Dev" + " - " + QString::number( labels[i], 'i', 0 ) + " Std Dev"; } } else { label = QString::number( lower, 'f', 4 ) + " - " + QString::number( upper, 'f', 4 ); } QgsSymbolV2* newSymbol = symbol->clone(); double colorValue = ( breaks.count() > 1 ? ( double ) i / ( breaks.count() - 1 ) : 0 ); newSymbol->setColor( ramp->color( colorValue ) ); // color from (0 / cl-1) to (cl-1 / cl-1) ranges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) ); } QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges ); r->setSourceSymbol( symbol->clone() ); r->setSourceColorRamp( ramp->clone() ); r->setMode( mode ); return r; }
bool QgsGeometryAnalyzer::convexHull( QgsVectorLayer* layer, const QString& shapefileName, bool onlySelectedFeatures, int uniqueIdField, QProgressDialog* p ) { if ( !layer ) { return false; } QgsVectorDataProvider* dp = layer->dataProvider(); if ( !dp ) { return false; } bool useField = false; if ( uniqueIdField == -1 ) { uniqueIdField = 0; } else { useField = true; } QgsFields fields; fields.append( QgsField( QString( "UID" ), QVariant::String ) ); fields.append( QgsField( QString( "AREA" ), QVariant::Double ) ); fields.append( QgsField( QString( "PERIM" ), QVariant::Double ) ); QGis::WkbType outputType = QGis::WKBPolygon; QgsCoordinateReferenceSystem crs = layer->crs(); QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, &crs ); QgsFeature currentFeature; QgsGeometry* dissolveGeometry = nullptr; //dissolve geometry QMultiMap<QString, QgsFeatureId> map; if ( onlySelectedFeatures ) { //use QgsVectorLayer::featureAtId const QgsFeatureIds selection = layer->selectedFeaturesIds(); QgsFeatureIds::const_iterator it = selection.constBegin(); for ( ; it != selection.constEnd(); ++it ) { #if 0 if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { // break; // it may be better to do something else here? return false; } #endif if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) ) { continue; } map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); } } else { QgsFeatureIterator fit = layer->getFeatures(); while ( fit.nextFeature( currentFeature ) ) { #if 0 if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { // break; // it may be better to do something else here? return false; } #endif map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); } } QMultiMap<QString, QgsFeatureId>::const_iterator jt = map.constBegin(); while ( jt != map.constEnd() ) { QString currentKey = jt.key(); int processedFeatures = 0; //take only selection if ( onlySelectedFeatures ) { //use QgsVectorLayer::featureAtId const QgsFeatureIds selection = layer->selectedFeaturesIds(); if ( p ) { p->setMaximum( selection.size() ); } processedFeatures = 0; while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) { if ( p && p->wasCanceled() ) { break; } if ( selection.contains( jt.value() ) ) { if ( p ) { p->setValue( processedFeatures ); } if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) { continue; } convexFeature( currentFeature, processedFeatures, &dissolveGeometry ); ++processedFeatures; } ++jt; } QList<double> values; if ( !dissolveGeometry ) { QgsDebugMsg( "no dissolved geometry - should not happen" ); return false; } dissolveGeometry = dissolveGeometry->convexHull(); values = simpleMeasure( dissolveGeometry ); QgsAttributes attributes( 3 ); attributes[0] = QVariant( currentKey ); attributes[1] = values.at( 0 ); attributes[2] = values.at( 1 ); QgsFeature dissolveFeature; dissolveFeature.setAttributes( attributes ); dissolveFeature.setGeometry( dissolveGeometry ); vWriter.addFeature( dissolveFeature ); } //take all features else { int featureCount = layer->featureCount(); if ( p ) { p->setMaximum( featureCount ); } processedFeatures = 0; while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) { if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { break; } if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) { continue; } convexFeature( currentFeature, processedFeatures, &dissolveGeometry ); ++processedFeatures; ++jt; } QList<double> values; // QgsGeometry* tmpGeometry = 0; if ( !dissolveGeometry ) { QgsDebugMsg( "no dissolved geometry - should not happen" ); return false; } dissolveGeometry = dissolveGeometry->convexHull(); // values = simpleMeasure( tmpGeometry ); values = simpleMeasure( dissolveGeometry ); QgsAttributes attributes; attributes[0] = QVariant( currentKey ); attributes[1] = QVariant( values[ 0 ] ); attributes[2] = QVariant( values[ 1 ] ); QgsFeature dissolveFeature; dissolveFeature.setAttributes( attributes ); dissolveFeature.setGeometry( dissolveGeometry ); vWriter.addFeature( dissolveFeature ); } } return true; }
bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature ) { feature.setValid( false ); if ( mClosed ) return false; if ( mFeatureQueue.empty() ) { QString fetch = QString( "FETCH FORWARD %1 FROM %2" ).arg( mFeatureQueueSize ).arg( mCursorName ); QgsDebugMsgLevel( QString( "fetching %1 features." ).arg( mFeatureQueueSize ), 4 ); if ( mConn->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously { QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) ); } QgsPostgresResult queryResult; for ( ;; ) { queryResult = mConn->PQgetResult(); if ( !queryResult.result() ) break; if ( queryResult.PQresultStatus() != PGRES_TUPLES_OK ) { QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) ); break; } int rows = queryResult.PQntuples(); if ( rows == 0 ) continue; for ( int row = 0; row < rows; row++ ) { mFeatureQueue.enqueue( QgsFeature() ); getFeature( queryResult, row, mFeatureQueue.back() ); } // for each row in queue } } if ( mFeatureQueue.empty() ) { QgsDebugMsg( QString( "Finished after %1 features" ).arg( mFetched ) ); close(); mSource->mShared->ensureFeaturesCountedAtLeast( mFetched ); return false; } // Now return the next feature from the queue if ( !mFetchGeometry ) { feature.setGeometryAndOwnership( 0, 0 ); } else { QgsGeometry* featureGeom = mFeatureQueue.front().geometryAndOwnership(); feature.setGeometry( featureGeom ); } feature.setFeatureId( mFeatureQueue.front().id() ); feature.setAttributes( mFeatureQueue.front().attributes() ); mFeatureQueue.dequeue(); mFetched++; feature.setValid( true ); feature.setFields( &mSource->mFields ); // allow name-based attribute lookups return true; }
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, bool doContains, bool doDifference, bool singleSelect ) { if ( selectGeometry->type() != QGis::Polygon ) { return; } QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( vlayer == NULL ) { return; } // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. QgsGeometry selectGeomTrans( *selectGeometry ); if ( canvas->mapSettings().hasCrsTransformEnabled() ) { try { QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs() ); selectGeomTrans.transform( ct ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and leave existing selection unchanged QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) ); QgisApp::instance()->messageBar()->pushMessage( QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system" ), QgsMessageBar::WARNING, QgisApp::instance()->messageTimeout() ); return; } } QApplication::setOverrideCursor( Qt::WaitCursor ); QgsDebugMsg( "Selection layer: " + vlayer->name() ); QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() ); QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) ); QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) ); QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() ); QgsFeatureRendererV2* r = vlayer->rendererV2(); if ( r ) r->startRender( context, vlayer->fields() ); QgsFeatureRequest request; request.setFilterRect( selectGeomTrans.boundingBox() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); if ( r ) request.setSubsetOfAttributes( r->usedAttributes(), vlayer->fields() ); else request.setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator fit = vlayer->getFeatures( request ); QgsFeatureIds newSelectedFeatures; QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { // make sure to only use features that are visible if ( r && !r->willRenderFeature( f ) ) continue; const QgsGeometry* g = f.constGeometry(); if ( doContains ) { if ( !selectGeomTrans.contains( g ) ) continue; } else { if ( !selectGeomTrans.intersects( g ) ) continue; } if ( singleSelect ) { foundSingleFeature = true; double distance = g->distance( selectGeomTrans ); if ( distance <= closestFeatureDist ) { closestFeatureDist = distance; closestFeatureId = f.id(); } } else { newSelectedFeatures.insert( f.id() ); } } if ( singleSelect && foundSingleFeature ) { newSelectedFeatures.insert( closestFeatureId ); } if ( r ) r->stopRender( context ); QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) ); if ( doDifference ) { QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds(); QgsFeatureIds selectedFeatures; QgsFeatureIds deselectedFeatures; QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd(); while ( i != newSelectedFeatures.constBegin() ) { --i; if ( layerSelectedFeatures.contains( *i ) ) { deselectedFeatures.insert( *i ); } else { selectedFeatures.insert( *i ); } } vlayer->modifySelection( selectedFeatures, deselectedFeatures ); } else { vlayer->setSelectedFeatures( newSelectedFeatures ); } QApplication::restoreOverrideCursor(); }
bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int row, QgsFeature &feature ) { feature.initAttributes( mSource->mFields.count() ); int col = 0; if ( mFetchGeometry ) { int returnedLength = ::PQgetlength( queryResult.result(), row, col ); if ( returnedLength > 0 ) { unsigned char *featureGeom = new unsigned char[returnedLength + 1]; memcpy( featureGeom, PQgetvalue( queryResult.result(), row, col ), returnedLength ); memset( featureGeom + returnedLength, 0, 1 ); // modify 2.5D WKB types to make them compliant with OGR unsigned int wkbType; memcpy( &wkbType, featureGeom + 1, sizeof( wkbType ) ); wkbType = QgsPostgresConn::wkbTypeFromOgcWkbType( wkbType ); memcpy( featureGeom + 1, &wkbType, sizeof( wkbType ) ); // change wkb type of inner geometries if ( wkbType == QGis::WKBMultiPoint25D || wkbType == QGis::WKBMultiLineString25D || wkbType == QGis::WKBMultiPolygon25D ) { unsigned int numGeoms; memcpy( &numGeoms, featureGeom + 5, sizeof( unsigned int ) ); unsigned char *wkb = featureGeom + 9; for ( unsigned int i = 0; i < numGeoms; ++i ) { unsigned int localType; memcpy( &localType, wkb + 1, sizeof( localType ) ); localType = QgsPostgresConn::wkbTypeFromOgcWkbType( localType ); memcpy( wkb + 1, &localType, sizeof( localType ) ); // skip endian and type info wkb += sizeof( unsigned int ) + 1; // skip coordinates switch ( wkbType ) { case QGis::WKBMultiPoint25D: wkb += sizeof( double ) * 3; break; case QGis::WKBMultiLineString25D: { unsigned int nPoints; memcpy( &nPoints, wkb, sizeof( int ) ); wkb += sizeof( int ) + sizeof( double ) * 3 * nPoints; } break; default: case QGis::WKBMultiPolygon25D: { unsigned int nRings; memcpy( &nRings, wkb, sizeof( int ) ); wkb += sizeof( int ); for ( unsigned int j = 0; j < nRings; ++j ) { unsigned int nPoints; memcpy( &nPoints, wkb, sizeof( int ) ); wkb += sizeof( nPoints ) + sizeof( double ) * 3 * nPoints; } } break; } } } feature.setGeometryAndOwnership( featureGeom, returnedLength + 1 ); } else { feature.setGeometryAndOwnership( 0, 0 ); } col++; } QgsFeatureId fid = 0; bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes; const QgsAttributeList& fetchAttributes = mRequest.subsetOfAttributes(); switch ( mSource->mPrimaryKeyType ) { case pktOid: case pktTid: case pktInt: fid = mConn->getBinaryInt( queryResult, row, col++ ); if ( mSource->mPrimaryKeyType == pktInt && ( !subsetOfAttributes || fetchAttributes.contains( mSource->mPrimaryKeyAttrs[0] ) ) ) feature.setAttribute( mSource->mPrimaryKeyAttrs[0], fid ); break; case pktFidMap: { QList<QVariant> primaryKeyVals; foreach ( int idx, mSource->mPrimaryKeyAttrs ) { const QgsField &fld = mSource->mFields[idx]; QVariant v = QgsPostgresProvider::convertValue( fld.type(), queryResult.PQgetvalue( row, col ) ); primaryKeyVals << v; if ( !subsetOfAttributes || fetchAttributes.contains( idx ) ) feature.setAttribute( idx, v ); col++; } fid = mSource->mShared->lookupFid( QVariant( primaryKeyVals ) ); } break; case pktUnknown: Q_ASSERT( !"FAILURE: cannot get feature with unknown primary key" ); return false; } feature.setFeatureId( fid ); QgsDebugMsgLevel( QString( "fid=%1" ).arg( fid ), 4 ); // iterate attributes if ( subsetOfAttributes ) { foreach ( int idx, fetchAttributes ) getFeatureAttribute( idx, queryResult, row, col, feature ); } else { for ( int idx = 0; idx < mSource->mFields.count(); ++idx )
void QgsAttributeTableFilterModel::generateListOfVisibleFeatures() { if ( !layer() ) return; bool filter = false; QgsRectangle rect = mCanvas->mapSettings().mapToLayerCoordinates( layer(), mCanvas->extent() ); QgsRenderContext renderContext; renderContext.expressionContext() << QgsExpressionContextUtils::globalScope() << QgsExpressionContextUtils::projectScope() << QgsExpressionContextUtils::layerScope( layer() ); QgsFeatureRendererV2* renderer = layer()->rendererV2(); mFilteredFeatures.clear(); if ( !renderer ) { QgsDebugMsg( "Cannot get renderer" ); return; } const QgsMapSettings& ms = mCanvas->mapSettings(); if ( layer()->hasScaleBasedVisibility() && ( layer()->minimumScale() > ms.scale() || layer()->maximumScale() <= ms.scale() ) ) { QgsDebugMsg( "Out of scale limits" ); } else { if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent ) { // setup scale // mapRenderer()->renderContext()->scale is not automaticaly updated when // render extent changes (because it's scale is used to identify if changed // since last render) -> use local context renderContext.setExtent( ms.visibleExtent() ); renderContext.setMapToPixel( ms.mapToPixel() ); renderContext.setRendererScale( ms.scale() ); } filter = renderer && renderer->capabilities() & QgsFeatureRendererV2::Filter; } renderer->startRender( renderContext, layer()->fields() ); QgsFeatureRequest r( masterModel()->request() ); if ( !r.filterRect().isNull() ) { r.setFilterRect( r.filterRect().intersect( &rect ) ); } else { r.setFilterRect( rect ); } QgsFeatureIterator features = masterModel()->layerCache()->getFeatures( r ); QgsFeature f; while ( features.nextFeature( f ) ) { renderContext.expressionContext().setFeature( f ); if ( !filter || renderer->willRenderFeature( f, renderContext ) ) { mFilteredFeatures << f.id(); } #if 0 if ( t.elapsed() > 5000 ) { bool cancel = false; emit progress( i, cancel ); if ( cancel ) break; t.restart(); } #endif } features.close(); if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent ) { renderer->stopRender( renderContext ); } }
bool QgsAfsProvider::getFeature( const QgsFeatureId &id, QgsFeature &f, bool fetchGeometry, const QList<int>& /*fetchAttributes*/, const QgsRectangle filterRect ) { // If cached, return cached feature QMap<QgsFeatureId, QgsFeature>::const_iterator it = mCache.find( id ); if ( it != mCache.end() ) { f = it.value(); return filterRect.isNull() || f.geometry()->intersects( filterRect ); } // Determine attributes to fetch /*QStringList fetchAttribNames; foreach ( int idx, fetchAttributes ) fetchAttribNames.append( mFields.at( idx ).name() ); */ // When fetching from server, fetch all attributes and geometry by default so that we can cache them QStringList fetchAttribNames; QList<int> fetchAttribIdx; for ( int idx = 0, n = mFields.size(); idx < n; ++idx ) { fetchAttribNames.append( mFields.at( idx ).name() ); fetchAttribIdx.append( idx ); } fetchGeometry = true; // Fetch 100 features at the time int startId = ( id / 100 ) * 100; int stopId = qMin( startId + 100, mObjectIds.length() ); QList<quint32> objectIds; for ( int i = startId; i < stopId; ++i ) { objectIds.append( mObjectIds[i] ); } // Query QString errorTitle, errorMessage; QVariantMap queryData = QgsArcGisRestUtils::getObjects( mDataSource.param( "url" ), objectIds, mDataSource.param( "crs" ), fetchGeometry, fetchAttribNames, QgsWKBTypes::hasM( mGeometryType ), QgsWKBTypes::hasZ( mGeometryType ), filterRect, errorTitle, errorMessage ); if ( queryData.isEmpty() ) { const_cast<QgsAfsProvider*>( this )->pushError( errorTitle + ": " + errorMessage ); QgsDebugMsg( "Query returned empty result" ); return false; } QVariantList featuresData = queryData["features"].toList(); if ( featuresData.isEmpty() ) { QgsDebugMsg( "Query returned no features" ); return false; } for ( int i = 0, n = featuresData.size(); i < n; ++i ) { QVariantMap featureData = featuresData[i].toMap(); QgsFeature feature; // Set FID feature.setFeatureId( startId + i ); // Set attributes if ( !fetchAttribIdx.isEmpty() ) { QVariantMap attributesData = featureData["attributes"].toMap(); feature.setFields( mFields ); QgsAttributes attributes( mFields.size() ); foreach ( int idx, fetchAttribIdx ) { attributes[idx] = attributesData[mFields.at( idx ).name()]; } feature.setAttributes( attributes ); } // Set geometry if ( fetchGeometry ) { QVariantMap geometryData = featureData["geometry"].toMap(); QgsAbstractGeometryV2* geometry = QgsArcGisRestUtils::parseEsriGeoJSON( geometryData, queryData["geometryType"].toString(), QgsWKBTypes::hasM( mGeometryType ), QgsWKBTypes::hasZ( mGeometryType ) ); // Above might return 0, which is ok since in theory empty geometries are allowed feature.setGeometry( new QgsGeometry( geometry ) ); } feature.setValid( true ); mCache.insert( feature.id(), feature ); }
void QgsMapToolNodeTool::createTopologyRubberBands( QgsVectorLayer* vlayer, const QList<QgsVertexEntry*> &vertexMap, int vertex ) { QMultiMap<double, QgsSnappingResult> currentResultList; QgsGeometry *geometry = mSelectedFeature->geometry(); // snap from current vertex currentResultList.clear(); vlayer->snapWithContext( vertexMap[vertex]->point(), ZERO_TOLERANCE, currentResultList, QgsSnapper::SnapToVertex ); QMultiMap<double, QgsSnappingResult>::iterator resultIt = currentResultList.begin(); for ( ; resultIt != currentResultList.end(); ++resultIt ) { // move all other if ( mSelectedFeature->featureId() != resultIt.value().snappedAtGeometry ) { if ( mTopologyMovingVertexes.contains( resultIt.value().snappedAtGeometry ) ) { if ( mTopologyMovingVertexes[resultIt.value().snappedAtGeometry]->contains( resultIt.value().snappedVertexNr ) ) { // skip vertex already exists in some rubberband continue; } } QgsRubberBand* trb = new QgsRubberBand( mCanvas, QGis::Line ); mTopologyRubberBand.append( trb ); int rbId = mTopologyRubberBand.size() - 1; trb->setWidth( 1 ); trb->setColor( Qt::red ); int tVertex = resultIt.value().snappedVertexNr; int tVertexBackup = -1, tVertexAfter = -1; int tVertexFirst = tVertex; // vertex number to check for cycling QgsFeature topolFeature; vlayer->getFeatures( QgsFeatureRequest().setFilterFid( resultIt.value().snappedAtGeometry ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( topolFeature ); QgsGeometry* topolGeometry = topolFeature.geometry(); while ( tVertex != -1 ) // looking for first vertex to rubberband { tVertexBackup = tVertex; topolGeometry->adjacentVertices( tVertex, tVertex, tVertexAfter ); if ( tVertex == -1 || tVertex == tVertexFirst ) break; // check if this is not first vertex of the feature or cycling error // if closest vertex is not from selected feature or is not selected end double dist; QgsPoint point = topolGeometry->vertexAt( tVertex ); int at, before, after; geometry->closestVertex( point, at, before, after, dist ); if ( dist > ZERO_TOLERANCE || !vertexMap[at]->isSelected() ) // problem with double precision { break; // found first vertex } } int movingPointIndex = 0; Vertexes* movingPoints = new Vertexes(); Vertexes* addedPoints = new Vertexes(); if ( mTopologyMovingVertexes.contains( resultIt.value().snappedAtGeometry ) ) { addedPoints = mTopologyMovingVertexes[ resultIt.value().snappedAtGeometry ]; } if ( tVertex == -1 ) // adding first point if needed { tVertex = tVertexBackup; } else { trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) ); if ( tVertex == tVertexFirst ) // cycle first vertex need to be added also { movingPoints->insert( movingPointIndex ); } movingPointIndex = 1; topolGeometry->adjacentVertices( tVertex, tVertexAfter, tVertex ); } while ( tVertex != -1 ) { // if closest vertex is not from selected feature or is not selected end double dist; QgsPoint point = topolGeometry->vertexAt( tVertex ); int at, before, after; geometry->closestVertex( point, at, before, after, dist ); // find first no matching vertex if ( dist > ZERO_TOLERANCE || !vertexMap[at]->isSelected() ) // problem with double precision { trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) ); break; // found first vertex } else // add moving point to rubberband { if ( addedPoints->contains( tVertex ) ) break; // just preventing to circle trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) ); movingPoints->insert( movingPointIndex ); movingPointIndex++; addedPoints->insert( tVertex ); } topolGeometry->adjacentVertices( tVertex, tVertexAfter, tVertex ); } mTopologyMovingVertexes.insert( resultIt.value().snappedAtGeometry, addedPoints ); mTopologyRubberBandVertexes.insert( rbId, movingPoints ); } } }
void QgsMapToolRotateFeature::canvasReleaseEvent( QgsMapMouseEvent *e ) { deleteRotationWidget(); if ( !mCanvas ) { return; } QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer ) { deleteRubberband(); notifyNotVectorLayer(); return; } if ( e->button() == Qt::RightButton ) { deleteRubberband(); mRotationActive = false; return; } // place anchor point on CTRL + click if ( e->modifiers() & Qt::ControlModifier ) { if ( !mAnchorPoint ) { return; } mAnchorPoint->setCenter( toMapCoordinates( e->pos() ) ); mStartPointMapCoords = toMapCoordinates( e->pos() ); mStPoint = e->pos(); return; } // Initialize rotation if not yet active if ( !mRotationActive ) { mRotation = 0; mRotationOffset = 0; deleteRubberband(); mInitialPos = e->pos(); if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } QgsPointXY layerCoords = toLayerCoordinates( vlayer, e->pos() ); double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() ); QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius, layerCoords.x() + searchRadius, layerCoords.y() + searchRadius ); if ( vlayer->selectedFeatureCount() == 0 ) { QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setSubsetOfAttributes( QgsAttributeList() ) ); //find the closest feature QgsGeometry pointGeometry = QgsGeometry::fromPoint( layerCoords ); if ( pointGeometry.isNull() ) { return; } double minDistance = std::numeric_limits<double>::max(); QgsFeature cf; QgsFeature f; while ( fit.nextFeature( f ) ) { if ( f.hasGeometry() ) { double currentDistance = pointGeometry.distance( f.geometry() ); if ( currentDistance < minDistance ) { minDistance = currentDistance; cf = f; } } } if ( minDistance == std::numeric_limits<double>::max() ) { emit messageEmitted( tr( "Could not find a nearby feature in the current layer." ) ); return; } QgsRectangle bound = cf.geometry().boundingBox(); mStartPointMapCoords = toMapCoordinates( vlayer, bound.center() ); if ( !mAnchorPoint ) { mAnchorPoint = new QgsVertexMarker( mCanvas ); } mAnchorPoint->setIconType( QgsVertexMarker::ICON_CROSS ); mAnchorPoint->setCenter( mStartPointMapCoords ); mStPoint = toCanvasCoordinates( mStartPointMapCoords ); mRotatedFeatures.clear(); mRotatedFeatures << cf.id(); //todo: take the closest feature, not the first one... mRubberBand = createRubberBand( vlayer->geometryType() ); mRubberBand->setToGeometry( cf.geometry(), vlayer ); } else { mRotatedFeatures = vlayer->selectedFeatureIds(); mRubberBand = createRubberBand( vlayer->geometryType() ) ; QgsFeature feat; QgsFeatureIterator it = vlayer->getSelectedFeatures(); while ( it.nextFeature( feat ) ) { mRubberBand->addGeometry( feat.geometry(), vlayer ); } } mRubberBand->setColor( QColor( 255, 0, 0, 65 ) ); mRubberBand->setWidth( 2 ); mRubberBand->show(); double XDistance = mInitialPos.x() - mAnchorPoint->x(); double YDistance = mInitialPos.y() - mAnchorPoint->y() ; mRotationOffset = std::atan2( YDistance, XDistance ) * ( 180 / M_PI ); createRotationWidget(); if ( e->modifiers() & Qt::ShiftModifier ) { if ( mRotationWidget ) { mRotationWidget->setMagnet( 45 ); } } mRotationActive = true; return; } applyRotation( mRotation ); }
bool QgsGeometryAnalyzer::extent( QgsVectorLayer* layer, const QString& shapefileName, bool onlySelectedFeatures, QProgressDialog * ) { if ( !layer ) { return false; } QgsVectorDataProvider* dp = layer->dataProvider(); if ( !dp ) { return false; } QGis::WkbType outputType = QGis::WKBPolygon; QgsCoordinateReferenceSystem crs = layer->crs(); QgsFields fields; fields.append( QgsField( QString( "MINX" ), QVariant::Double ) ); fields.append( QgsField( QString( "MINY" ), QVariant::Double ) ); fields.append( QgsField( QString( "MAXX" ), QVariant::Double ) ); fields.append( QgsField( QString( "MAXY" ), QVariant::Double ) ); fields.append( QgsField( QString( "CNTX" ), QVariant::Double ) ); fields.append( QgsField( QString( "CNTY" ), QVariant::Double ) ); fields.append( QgsField( QString( "AREA" ), QVariant::Double ) ); fields.append( QgsField( QString( "PERIM" ), QVariant::Double ) ); fields.append( QgsField( QString( "HEIGHT" ), QVariant::Double ) ); fields.append( QgsField( QString( "WIDTH" ), QVariant::Double ) ); QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, &crs ); QgsRectangle rect; if ( onlySelectedFeatures ) // take only selection { rect = layer->boundingBoxOfSelected(); } else { rect = layer->extent(); } double minx = rect.xMinimum(); double miny = rect.yMinimum(); double maxx = rect.xMaximum(); double maxy = rect.yMaximum(); double height = rect.height(); double width = rect.width(); double cntx = minx + ( width / 2.0 ); double cnty = miny + ( height / 2.0 ); double area = width * height; double perim = ( 2 * width ) + ( 2 * height ); QgsFeature feat; QgsAttributes attrs( 10 ); attrs[0] = QVariant( minx ); attrs[1] = QVariant( miny ); attrs[2] = QVariant( maxx ); attrs[3] = QVariant( maxy ); attrs[4] = QVariant( cntx ); attrs[5] = QVariant( cnty ); attrs[6] = QVariant( area ); attrs[7] = QVariant( perim ); attrs[8] = QVariant( height ); attrs[9] = QVariant( width ); feat.setAttributes( attrs ); feat.setGeometry( QgsGeometry::fromRect( rect ) ); vWriter.addFeature( feat ); return true; }
void QgsDelimitedTextProvider::scanFile( bool buildIndexes ) { QStringList messages; // assume the layer is invalid until proven otherwise mLayerValid = false; mValid = false; mRescanRequired = false; clearInvalidLines(); // Initiallize indexes resetIndexes(); bool buildSpatialIndex = buildIndexes && nullptr != mSpatialIndex; // No point building a subset index if there is no geometry, as all // records will be included. bool buildSubsetIndex = buildIndexes && mBuildSubsetIndex && mGeomRep != GeomNone; if ( ! mFile->isValid() ) { // uri is invalid so the layer must be too... messages.append( tr( "File cannot be opened or delimiter parameters are not valid" ) ); reportErrors( messages ); QgsDebugMsg( "Delimited text source invalid - filename or delimiter parameters" ); return; } // Open the file and get number of rows, etc. We assume that the // file has a header row and process accordingly. Caller should make // sure that the delimited file is properly formed. if ( mGeomRep == GeomAsWkt ) { mWktFieldIndex = mFile->fieldIndex( mWktFieldName ); if ( mWktFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "Wkt" ), mWktFieldName ) ); } } else if ( mGeomRep == GeomAsXy ) { mXFieldIndex = mFile->fieldIndex( mXFieldName ); mYFieldIndex = mFile->fieldIndex( mYFieldName ); if ( mXFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "X" ), mWktFieldName ) ); } if ( mYFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "Y" ), mWktFieldName ) ); } } if ( !messages.isEmpty() ) { reportErrors( messages ); QgsDebugMsg( "Delimited text source invalid - missing geometry fields" ); return; } // Scan the entire file to determine // 1) the number of fields (this is handled by QgsDelimitedTextFile mFile // 2) the number of valid features. Note that the selection of valid features // should match the code in QgsDelimitedTextFeatureIterator // 3) the geometric extents of the layer // 4) the type of each field // // Also build subset and spatial indexes. QStringList parts; long nEmptyRecords = 0; long nBadFormatRecords = 0; long nIncompatibleGeometry = 0; long nInvalidGeometry = 0; long nEmptyGeometry = 0; mNumberFeatures = 0; mExtent = QgsRectangle(); QList<bool> isEmpty; QList<bool> couldBeInt; QList<bool> couldBeLongLong; QList<bool> couldBeDouble; bool foundFirstGeometry = false; while ( true ) { QgsDelimitedTextFile::Status status = mFile->nextRecord( parts ); if ( status == QgsDelimitedTextFile::RecordEOF ) break; if ( status != QgsDelimitedTextFile::RecordOk ) { nBadFormatRecords++; recordInvalidLine( tr( "Invalid record format at line %1" ) ); continue; } // Skip over empty records if ( recordIsEmpty( parts ) ) { nEmptyRecords++; continue; } // Check geometries are valid bool geomValid = true; if ( mGeomRep == GeomAsWkt ) { if ( mWktFieldIndex >= parts.size() || parts[mWktFieldIndex].isEmpty() ) { nEmptyGeometry++; mNumberFeatures++; } else { // Get the wkt - confirm it is valid, get the type, and // if compatible with the rest of file, add to the extents QString sWkt = parts[mWktFieldIndex]; QgsGeometry geom; if ( !mWktHasPrefix && sWkt.indexOf( sWktPrefixRegexp ) >= 0 ) mWktHasPrefix = true; geom = geomFromWkt( sWkt, mWktHasPrefix ); if ( !geom.isNull() ) { QgsWkbTypes::Type type = geom.wkbType(); if ( type != QgsWkbTypes::NoGeometry ) { if ( mGeometryType == QgsWkbTypes::UnknownGeometry || geom.type() == mGeometryType ) { mGeometryType = geom.type(); if ( !foundFirstGeometry ) { mNumberFeatures++; mWkbType = type; mExtent = geom.boundingBox(); foundFirstGeometry = true; } else { mNumberFeatures++; if ( geom.isMultipart() ) mWkbType = type; QgsRectangle bbox( geom.boundingBox() ); mExtent.combineExtentWith( bbox ); } if ( buildSpatialIndex ) { QgsFeature f; f.setId( mFile->recordId() ); f.setGeometry( geom ); mSpatialIndex->insertFeature( f ); } } else { nIncompatibleGeometry++; geomValid = false; } } } else { geomValid = false; nInvalidGeometry++; recordInvalidLine( tr( "Invalid WKT at line %1" ) ); } } } else if ( mGeomRep == GeomAsXy ) { // Get the x and y values, first checking to make sure they // aren't null. QString sX = mXFieldIndex < parts.size() ? parts[mXFieldIndex] : QString(); QString sY = mYFieldIndex < parts.size() ? parts[mYFieldIndex] : QString(); if ( sX.isEmpty() && sY.isEmpty() ) { nEmptyGeometry++; mNumberFeatures++; } else { QgsPointXY pt; bool ok = pointFromXY( sX, sY, pt, mDecimalPoint, mXyDms ); if ( ok ) { if ( foundFirstGeometry ) { mExtent.combineExtentWith( pt.x(), pt.y() ); } else { // Extent for the first point is just the first point mExtent.set( pt.x(), pt.y(), pt.x(), pt.y() ); mWkbType = QgsWkbTypes::Point; mGeometryType = QgsWkbTypes::PointGeometry; foundFirstGeometry = true; } mNumberFeatures++; if ( buildSpatialIndex && std::isfinite( pt.x() ) && std::isfinite( pt.y() ) ) { QgsFeature f; f.setId( mFile->recordId() ); f.setGeometry( QgsGeometry::fromPointXY( pt ) ); mSpatialIndex->insertFeature( f ); } } else { geomValid = false; nInvalidGeometry++; recordInvalidLine( tr( "Invalid X or Y fields at line %1" ) ); } } } else { mWkbType = QgsWkbTypes::NoGeometry; mNumberFeatures++; } if ( !geomValid ) continue; if ( buildSubsetIndex ) mSubsetIndex.append( mFile->recordId() ); // If we are going to use this record, then assess the potential types of each column for ( int i = 0; i < parts.size(); i++ ) { QString &value = parts[i]; // Ignore empty fields - spreadsheet generated CSV files often // have random empty fields at the end of a row if ( value.isEmpty() ) continue; // Expand the columns to include this non empty field if necessary while ( couldBeInt.size() <= i ) { isEmpty.append( true ); couldBeInt.append( false ); couldBeLongLong.append( false ); couldBeDouble.append( false ); } // If this column has been empty so far then initiallize it // for possible types if ( isEmpty[i] ) { isEmpty[i] = false; couldBeInt[i] = true; couldBeLongLong[i] = true; couldBeDouble[i] = true; } if ( ! mDetectTypes ) { continue; } // Now test for still valid possible types for the field // Types are possible until first record which cannot be parsed if ( couldBeInt[i] ) { value.toInt( &couldBeInt[i] ); } if ( couldBeLongLong[i] && ! couldBeInt[i] ) { value.toLongLong( &couldBeLongLong[i] ); } if ( couldBeDouble[i] && ! couldBeLongLong[i] ) { if ( ! mDecimalPoint.isEmpty() ) { value.replace( mDecimalPoint, QLatin1String( "." ) ); } value.toDouble( &couldBeDouble[i] ); } } } // Now create the attribute fields. Field types are integer by preference, // failing that double, failing that text. QStringList fieldNames = mFile->fieldNames(); mFieldCount = fieldNames.size(); attributeColumns.clear(); attributeFields.clear(); QString csvtMessage; QStringList csvtTypes = readCsvtFieldTypes( mFile->fileName(), &csvtMessage ); for ( int i = 0; i < fieldNames.size(); i++ ) { // Skip over WKT field ... don't want to display in attribute table if ( i == mWktFieldIndex ) continue; // Add the field index lookup for the column attributeColumns.append( i ); QVariant::Type fieldType = QVariant::String; QString typeName = QStringLiteral( "text" ); if ( i < csvtTypes.size() ) { typeName = csvtTypes[i]; } else if ( mDetectTypes && i < couldBeInt.size() ) { if ( couldBeInt[i] ) { typeName = QStringLiteral( "integer" ); } else if ( couldBeLongLong[i] ) { typeName = QStringLiteral( "longlong" ); } else if ( couldBeDouble[i] ) { typeName = QStringLiteral( "double" ); } } if ( typeName == QStringLiteral( "integer" ) ) { fieldType = QVariant::Int; } else if ( typeName == QStringLiteral( "longlong" ) ) { fieldType = QVariant::LongLong; } else if ( typeName == QStringLiteral( "real" ) || typeName == QStringLiteral( "double" ) ) { typeName = QStringLiteral( "double" ); fieldType = QVariant::Double; } else { typeName = QStringLiteral( "text" ); } attributeFields.append( QgsField( fieldNames[i], fieldType, typeName ) ); } QgsDebugMsg( "Field count for the delimited text file is " + QString::number( attributeFields.size() ) ); QgsDebugMsg( "geometry type is: " + QString::number( mWkbType ) ); QgsDebugMsg( "feature count is: " + QString::number( mNumberFeatures ) ); QStringList warnings; if ( ! csvtMessage.isEmpty() ) warnings.append( csvtMessage ); if ( nBadFormatRecords > 0 ) warnings.append( tr( "%1 records discarded due to invalid format" ).arg( nBadFormatRecords ) ); if ( nEmptyGeometry > 0 ) warnings.append( tr( "%1 records have missing geometry definitions" ).arg( nEmptyGeometry ) ); if ( nInvalidGeometry > 0 ) warnings.append( tr( "%1 records discarded due to invalid geometry definitions" ).arg( nInvalidGeometry ) ); if ( nIncompatibleGeometry > 0 ) warnings.append( tr( "%1 records discarded due to incompatible geometry types" ).arg( nIncompatibleGeometry ) ); reportErrors( warnings ); // Decide whether to use subset ids to index records rather than simple iteration through all // If more than 10% of records are being skipped, then use index. (Not based on any experimentation, // could do with some analysis?) if ( buildSubsetIndex ) { long recordCount = mFile->recordCount(); recordCount -= recordCount / SUBSET_ID_THRESHOLD_FACTOR; mUseSubsetIndex = mSubsetIndex.size() < recordCount; if ( ! mUseSubsetIndex ) mSubsetIndex = QList<quintptr>(); } mUseSpatialIndex = buildSpatialIndex; mValid = mGeometryType != QgsWkbTypes::UnknownGeometry; mLayerValid = mValid; // If it is valid, then watch for changes to the file connect( mFile.get(), &QgsDelimitedTextFile::fileUpdated, this, &QgsDelimitedTextProvider::onFileUpdated ); }
bool QgsGeometryAnalyzer::dissolve( QgsVectorLayer* layer, const QString& shapefileName, bool onlySelectedFeatures, int uniqueIdField, QProgressDialog* p ) { if ( !layer ) { return false; } QgsVectorDataProvider* dp = layer->dataProvider(); if ( !dp ) { return false; } bool useField = false; if ( uniqueIdField == -1 ) { uniqueIdField = 0; } else { useField = true; } QGis::WkbType outputType = dp->geometryType(); QgsCoordinateReferenceSystem crs = layer->crs(); QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, &crs ); QgsFeature currentFeature; QMultiMap<QString, QgsFeatureId> map; if ( onlySelectedFeatures ) { //use QgsVectorLayer::featureAtId const QgsFeatureIds selection = layer->selectedFeaturesIds(); QgsFeatureIds::const_iterator it = selection.constBegin(); for ( ; it != selection.constEnd(); ++it ) { if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) ) { continue; } map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); } } else { QgsFeatureIterator fit = layer->getFeatures(); while ( fit.nextFeature( currentFeature ) ) { map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); } } QgsGeometry *dissolveGeometry = nullptr; //dissolve geometry QMultiMap<QString, QgsFeatureId>::const_iterator jt = map.constBegin(); QgsFeature outputFeature; while ( jt != map.constEnd() ) { QString currentKey = jt.key(); int processedFeatures = 0; bool first = true; //take only selection if ( onlySelectedFeatures ) { //use QgsVectorLayer::featureAtId const QgsFeatureIds selection = layer->selectedFeaturesIds(); if ( p ) { p->setMaximum( selection.size() ); } while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) { if ( p && p->wasCanceled() ) { break; } if ( selection.contains( jt.value() ) ) { if ( p ) { p->setValue( processedFeatures ); } if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) { continue; } if ( first ) { outputFeature.setAttributes( currentFeature.attributes() ); first = false; } dissolveFeature( currentFeature, processedFeatures, &dissolveGeometry ); ++processedFeatures; } ++jt; } } //take all features else { int featureCount = layer->featureCount(); if ( p ) { p->setMaximum( featureCount ); } while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) { if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { break; } if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) { continue; } { outputFeature.setAttributes( currentFeature.attributes() ); first = false; } dissolveFeature( currentFeature, processedFeatures, &dissolveGeometry ); ++processedFeatures; ++jt; } } outputFeature.setGeometry( dissolveGeometry ); vWriter.addFeature( outputFeature ); } return true; }
void QgsDelimitedTextProvider::rescanFile() const { mRescanRequired = false; resetIndexes(); bool buildSpatialIndex = nullptr != mSpatialIndex; bool buildSubsetIndex = mBuildSubsetIndex && ( mSubsetExpression || mGeomRep != GeomNone ); // In case file has been rewritten check that it is still valid mValid = mLayerValid && mFile->isValid(); if ( ! mValid ) return; // Open the file and get number of rows, etc. We assume that the // file has a header row and process accordingly. Caller should make // sure that the delimited file is properly formed. QStringList messages; if ( mGeomRep == GeomAsWkt ) { mWktFieldIndex = mFile->fieldIndex( mWktFieldName ); if ( mWktFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "Wkt" ), mWktFieldName ) ); } } else if ( mGeomRep == GeomAsXy ) { mXFieldIndex = mFile->fieldIndex( mXFieldName ); mYFieldIndex = mFile->fieldIndex( mYFieldName ); if ( mXFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "X" ), mWktFieldName ) ); } if ( mYFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "Y" ), mWktFieldName ) ); } } if ( !messages.isEmpty() ) { reportErrors( messages ); QgsDebugMsg( "Delimited text source invalid on rescan - missing geometry fields" ); mValid = false; return; } // Reset the field columns for ( int i = 0; i < attributeFields.size(); i++ ) { attributeColumns[i] = mFile->fieldIndex( attributeFields.at( i ).name() ); } // Scan through the features in the file mSubsetIndex.clear(); mUseSubsetIndex = false; QgsFeatureIterator fi = getFeatures( QgsFeatureRequest() ); mNumberFeatures = 0; mExtent = QgsRectangle(); QgsFeature f; bool foundFirstGeometry = false; while ( fi.nextFeature( f ) ) { if ( mGeometryType != QgsWkbTypes::NullGeometry && f.hasGeometry() ) { if ( !foundFirstGeometry ) { mExtent = f.geometry().boundingBox(); foundFirstGeometry = true; } else { QgsRectangle bbox( f.geometry().boundingBox() ); mExtent.combineExtentWith( bbox ); } if ( buildSpatialIndex ) mSpatialIndex->insertFeature( f ); } if ( buildSubsetIndex ) mSubsetIndex.append( ( quintptr ) f.id() ); mNumberFeatures++; } if ( buildSubsetIndex ) { long recordCount = mFile->recordCount(); recordCount -= recordCount / SUBSET_ID_THRESHOLD_FACTOR; mUseSubsetIndex = recordCount < mSubsetIndex.size(); if ( ! mUseSubsetIndex ) mSubsetIndex.clear(); } mUseSpatialIndex = buildSpatialIndex; }
bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer* eventLayer, int lineField, int eventField, QgsFeatureIds &unlocatedFeatureIds, const QString& outputLayer, const QString& outputFormat, int locationField1, int locationField2, int offsetField, double offsetScale, bool forceSingleGeometry, QgsVectorDataProvider* memoryProvider, QProgressDialog* p ) { if ( !lineLayer || !eventLayer || !lineLayer->isValid() || !eventLayer->isValid() ) { return false; } //create line field / id map for line layer QMultiHash< QString, QgsFeature > lineLayerIdMap; //1:n possible (e.g. several linear reference geometries for one feature in the event layer) QgsFeatureIterator fit = lineLayer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() << lineField ) ); QgsFeature fet; while ( fit.nextFeature( fet ) ) { lineLayerIdMap.insert( fet.attribute( lineField ).toString(), fet ); } //create output datasource or attributes in memory provider QgsVectorFileWriter* fileWriter = nullptr; QgsFeatureList memoryProviderFeatures; if ( !memoryProvider ) { QGis::WkbType memoryProviderType = QGis::WKBMultiLineString; if ( locationField2 == -1 ) { memoryProviderType = forceSingleGeometry ? QGis::WKBPoint : QGis::WKBMultiPoint; } else { memoryProviderType = forceSingleGeometry ? QGis::WKBLineString : QGis::WKBMultiLineString; } fileWriter = new QgsVectorFileWriter( outputLayer, eventLayer->dataProvider()->encoding(), eventLayer->fields(), memoryProviderType, &( lineLayer->crs() ), outputFormat ); } else { memoryProvider->addAttributes( eventLayer->fields().toList() ); } //iterate over eventLayer and write new features to output file or layer fit = eventLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) ); QgsGeometry* lrsGeom = nullptr; double measure1, measure2 = 0.0; int nEventFeatures = eventLayer->featureCount(); int featureCounter = 0; int nOutputFeatures = 0; //number of output features for the current event feature if ( p ) { p->setWindowModality( Qt::WindowModal ); p->setMinimum( 0 ); p->setMaximum( nEventFeatures ); p->show(); } while ( fit.nextFeature( fet ) ) { nOutputFeatures = 0; //update progress dialog if ( p ) { if ( p->wasCanceled() ) { break; } p->setValue( featureCounter ); ++featureCounter; } measure1 = fet.attribute( locationField1 ).toDouble(); if ( locationField2 != -1 ) { measure2 = fet.attribute( locationField2 ).toDouble(); if ( qgsDoubleNear(( measure2 - measure1 ), 0.0 ) ) { continue; } } QList<QgsFeature> featureIdList = lineLayerIdMap.values( fet.attribute( eventField ).toString() ); QList<QgsFeature>::iterator featureIdIt = featureIdList.begin(); for ( ; featureIdIt != featureIdList.end(); ++featureIdIt ) { if ( locationField2 == -1 ) { lrsGeom = locateAlongMeasure( measure1, featureIdIt->constGeometry() ); } else { lrsGeom = locateBetweenMeasures( measure1, measure2, featureIdIt->constGeometry() ); } if ( lrsGeom ) { ++nOutputFeatures; addEventLayerFeature( fet, lrsGeom, featureIdIt->geometry(), fileWriter, memoryProviderFeatures, offsetField, offsetScale, forceSingleGeometry ); } } if ( nOutputFeatures < 1 ) { unlocatedFeatureIds.insert( fet.id() ); } } if ( p ) { p->setValue( nEventFeatures ); } if ( memoryProvider ) { memoryProvider->addFeatures( memoryProviderFeatures ); } delete fileWriter; return true; }
int QgsAtlasComposition::updateFeatures() { //needs to be called when layer, filter, sort changes if ( !mCoverageLayer ) { return 0; } updateFilenameExpression(); // select all features with all attributes QgsFeatureIterator fit = mCoverageLayer->getFeatures(); std::auto_ptr<QgsExpression> filterExpression; if ( mFilterFeatures && !mFeatureFilter.isEmpty() ) { filterExpression = std::auto_ptr<QgsExpression>( new QgsExpression( mFeatureFilter ) ); if ( filterExpression->hasParserError() ) { throw std::runtime_error( tr( "Feature filter parser error: %1" ).arg( filterExpression->parserErrorString() ).toLocal8Bit().data() ); } } // We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process // We thus store the feature ids for future extraction QgsFeature feat; mFeatureIds.clear(); mFeatureKeys.clear(); while ( fit.nextFeature( feat ) ) { if ( mFilterFeatures && !mFeatureFilter.isEmpty() ) { QVariant result = filterExpression->evaluate( &feat, mCoverageLayer->pendingFields() ); if ( filterExpression->hasEvalError() ) { throw std::runtime_error( tr( "Feature filter eval error: %1" ).arg( filterExpression->evalErrorString() ).toLocal8Bit().data() ); } // skip this feature if the filter evaluation if false if ( !result.toBool() ) { continue; } } mFeatureIds.push_back( feat.id() ); if ( mSortFeatures ) { mFeatureKeys.insert( feat.id(), feat.attributes()[ mSortKeyAttributeIdx ] ); } } // sort features, if asked for if ( mSortFeatures ) { FieldSorter sorter( mFeatureKeys, mSortAscending ); qSort( mFeatureIds.begin(), mFeatureIds.end(), sorter ); } QgsExpression::setSpecialColumn( "$numfeatures", QVariant(( int )mFeatureIds.size() ) ); //jump to first feature if currently using an atlas preview //need to do this in case filtering/layer change has altered matching features if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas ) { firstFeature(); } return mFeatureIds.size(); }
void QgsEditorWidgetWrapper::setFeature( const QgsFeature& feature ) { setValue( feature.attribute( mFieldIdx ) ); }
void QgsEditorWidgetWrapper::updateConstraint( const QgsVectorLayer *layer, int index, const QgsFeature &ft, QgsFieldConstraints::ConstraintOrigin constraintOrigin ) { QStringList errors; QStringList softErrors; QStringList expressions; QStringList descriptions; bool toEmit( false ); bool hardConstraintsOk( true ); bool softConstraintsOk( true ); QgsField field = layer->fields().at( index ); QString expression = field.constraints().constraintExpression(); if ( ft.isValid() ) { if ( ! expression.isEmpty() ) { expressions << expression; descriptions << field.constraints().constraintDescription(); toEmit = true; } if ( field.constraints().constraints() & QgsFieldConstraints::ConstraintNotNull ) { descriptions << tr( "Not NULL" ); if ( !expression.isEmpty() ) { expressions << field.name() + QStringLiteral( " IS NOT NULL" ); } else { expressions << QStringLiteral( "IS NOT NULL" ); } toEmit = true; } if ( field.constraints().constraints() & QgsFieldConstraints::ConstraintUnique ) { descriptions << tr( "Unique" ); if ( !expression.isEmpty() ) { expressions << field.name() + QStringLiteral( " IS UNIQUE" ); } else { expressions << QStringLiteral( "IS UNIQUE" ); } toEmit = true; } hardConstraintsOk = QgsVectorLayerUtils::validateAttribute( layer, ft, index, errors, QgsFieldConstraints::ConstraintStrengthHard, constraintOrigin ); softConstraintsOk = QgsVectorLayerUtils::validateAttribute( layer, ft, index, softErrors, QgsFieldConstraints::ConstraintStrengthSoft, constraintOrigin ); errors << softErrors; } else // invalid feature { if ( ! expression.isEmpty() ) { hardConstraintsOk = true; softConstraintsOk = false; errors << QStringLiteral( "Invalid feature" ); toEmit = true; } } mValidConstraint = hardConstraintsOk && softConstraintsOk; mIsBlockingCommit = !hardConstraintsOk; mConstraintFailureReason = errors.join( QStringLiteral( ", " ) ); if ( toEmit ) { QString errStr = errors.isEmpty() ? tr( "Constraint checks passed" ) : mConstraintFailureReason; QString description = descriptions.join( QStringLiteral( ", " ) ); QString expressionDesc; if ( expressions.size() > 1 ) expressionDesc = "( " + expressions.join( QStringLiteral( " ) AND ( " ) ) + " )"; else if ( !expressions.isEmpty() ) expressionDesc = expressions.at( 0 ); ConstraintResult result = !hardConstraintsOk ? ConstraintResultFailHard : ( !softConstraintsOk ? ConstraintResultFailSoft : ConstraintResultPass ); //set the constraint result mConstraintResult = result; updateConstraintWidgetStatus(); emit constraintStatusChanged( expressionDesc, description, errStr, result ); } }
/** * This method is an extension of the constructor. It was implemented to reduce the amount of code duplicated between the constructors. */ bool eVisGenericEventBrowserGui::initBrowser() { //setup gui setWindowTitle( tr( "Generic Event Browser" ) ); connect( treeEventData, SIGNAL( itemDoubleClicked( QTreeWidgetItem *, int ) ), this, SLOT( launchExternalApplication( QTreeWidgetItem *, int ) ) ); mHighlightSymbol.load( QStringLiteral( ":/evis/eVisHighlightSymbol.png" ) ); mPointerSymbol.load( QStringLiteral( ":/evis/eVisPointerSymbol.png" ) ); mCompassOffset = 0.0; //Flag to let us know if the browser fully loaded mBrowserInitialized = false; //Initialize some class variables mDefaultEventImagePathField = 0; mDefaultCompassBearingField = 0; mDefaultCompassOffsetField = 0; //initialize Display tab GUI elements pbtnNext->setEnabled( false ); pbtnPrevious->setEnabled( false ); //Set up Attribute display treeEventData->setColumnCount( 2 ); QStringList treeHeaders; treeHeaders << tr( "Field" ) << tr( "Value" ); treeEventData->setHeaderLabels( treeHeaders ); //Initialize Options tab GUI elements cboxEventImagePathField->setEnabled( true ); chkboxEventImagePathRelative->setChecked( false ); chkboxDisplayCompassBearing->setChecked( false ); cboxCompassBearingField->setEnabled( true ); rbtnManualCompassOffset->setChecked( false ); dsboxCompassOffset->setEnabled( true ); dsboxCompassOffset->setValue( 0.0 ); rbtnAttributeCompassOffset->setChecked( false ); cboxCompassOffsetField->setEnabled( true ); chkboxUseOnlyFilename->setChecked( false ); QString myThemePath = QgsApplication::activeThemePath(); pbtnResetEventImagePathData->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) ); pbtnResetCompassBearingData->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) ); pbtnResetCompassOffsetData->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) ); pbtnResetBasePathData->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) ); pbtnResetUseOnlyFilenameData->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) ); pbtnResetApplyPathRulesToDocs->setIcon( QIcon( QPixmap( myThemePath + "/mActionDraw.svg" ) ) ); chkboxSaveEventImagePathData->setChecked( false ); chkboxSaveCompassBearingData->setChecked( false ); chkboxSaveCompassOffsetData->setChecked( false ); chkboxSaveBasePathData->setChecked( false ); chkboxSaveUseOnlyFilenameData->setChecked( false ); //Set up Configure External Application buttons pbtnAddFileType->setIcon( QIcon( QPixmap( myThemePath + "/mActionNewAttribute.svg" ) ) ); pbtnDeleteFileType->setIcon( QIcon( QPixmap( myThemePath + "/mActionDeleteAttribute.svg" ) ) ); //Check to for interface, not null when launched from plugin toolbar, otherwise expect map canvas if ( mInterface ) { //check for active layer if ( mInterface->activeLayer() ) { //verify that the active layer is a vector layer if ( QgsMapLayer::VectorLayer == mInterface->activeLayer()->type() ) { mVectorLayer = ( QgsVectorLayer* )mInterface->activeLayer(); mCanvas = mInterface->mapCanvas(); } else { QMessageBox::warning( this, tr( "Warning" ), tr( "This tool only supports vector data" ) ); return false; } } else { QMessageBox::warning( this, tr( "Warning" ), tr( "No active layers found" ) ); return false; } } //check for map canvas, if map canvas is null, throw error else if ( mCanvas ) { //check for active layer if ( mCanvas->currentLayer() ) { //verify that the active layer is a vector layer if ( QgsMapLayer::VectorLayer == mCanvas->currentLayer()->type() ) { mVectorLayer = ( QgsVectorLayer* )mCanvas->currentLayer(); } else { QMessageBox::warning( this, tr( "Warning" ), tr( "This tool only supports vector data" ) ); return false; } } else { QMessageBox::warning( this, tr( "Warning" ), tr( "No active layers found" ) ); return false; } } else { QMessageBox::warning( this, tr( "Error" ), tr( "Unable to connect to either the map canvas or application interface" ) ); return false; } //Connect rendering routine for highlighting symbols and load symbols connect( mCanvas, SIGNAL( renderComplete( QPainter * ) ), this, SLOT( renderSymbol( QPainter * ) ) ); mDataProvider = mVectorLayer->dataProvider(); /* * A list of the selected feature ids is made so that we can move forward and backward through * the list. The data providers only have the ability to get one feature at a time or * sequentially move forward through the selected features */ if ( 0 == mVectorLayer->selectedFeatureCount() ) //if nothing is selected select everything { mVectorLayer->invertSelection(); mFeatureIds = mVectorLayer->selectedFeatureIds().toList(); } else //use selected features { mFeatureIds = mVectorLayer->selectedFeatureIds().toList(); } if ( 0 == mFeatureIds.size() ) return false; //get the first feature in the list so we can set the field in the pulldown menues QgsFeature* myFeature = featureAtId( mFeatureIds.at( mCurrentFeatureIndex ) ); if ( !myFeature ) { QMessageBox::warning( this, tr( "Error" ), tr( "An invalid feature was received during initialization" ) ); return false; } QgsFields myFields = mDataProvider->fields(); mIgnoreEvent = true; //Ignore indexChanged event when adding items to combo boxes for ( int x = 0; x < myFields.count(); x++ ) { QString name = myFields.at( x ).name(); cboxEventImagePathField->addItem( name ); cboxCompassBearingField->addItem( name ); cboxCompassOffsetField->addItem( name ); if ( myFeature->attribute( x ).toString().contains( QRegExp( "(jpg|jpeg|tif|tiff|gif)", Qt::CaseInsensitive ) ) ) { mDefaultEventImagePathField = x; } if ( name.contains( QRegExp( "(comp|bear)", Qt::CaseInsensitive ) ) ) { mDefaultCompassBearingField = x; } if ( name.contains( QRegExp( "(offset|declination)", Qt::CaseInsensitive ) ) ) { mDefaultCompassOffsetField = x; } } mIgnoreEvent = false; //Set Display tab gui items if ( mFeatureIds.size() > 1 ) { pbtnNext->setEnabled( true ); } setWindowTitle( tr( "Event Browser - Displaying records 01 of %1" ).arg( mFeatureIds.size(), 2, 10, QChar( '0' ) ) ); //Set Options tab gui items initOptionsTab(); //Load file associations into Configure External Applications tab gui items QSettings myQSettings; myQSettings.beginWriteArray( QStringLiteral( "/eVis/filetypeassociations" ) ); int myTotalAssociations = myQSettings.childGroups().count(); int myIterator = 0; while ( myIterator < myTotalAssociations ) { myQSettings.setArrayIndex( myIterator ); tableFileTypeAssociations->insertRow( tableFileTypeAssociations->rowCount() ); tableFileTypeAssociations->setItem( myIterator, 0, new QTableWidgetItem( myQSettings.value( QStringLiteral( "extension" ), "" ).toString() ) ); tableFileTypeAssociations->setItem( myIterator, 1, new QTableWidgetItem( myQSettings.value( QStringLiteral( "application" ), "" ).toString() ) ); myIterator++; } myQSettings.endArray(); mBrowserInitialized = true; return true; }
void QgsLineVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, const QVector< QgsPoint >& additionalPoints, QVector< QgsPoint >& tiedPoint ) const { QgsVectorLayer *vl = mVectorLayer; if ( !vl ) return; int featureCount = ( int ) vl->featureCount() * 2; int step = 0; QgsCoordinateTransform ct; ct.setSourceCrs( vl->crs() ); if ( builder->coordinateTransformationEnabled() ) { ct.setDestinationCrs( builder->destinationCrs() ); } else { ct.setDestinationCrs( vl->crs() ); } tiedPoint = QVector< QgsPoint >( additionalPoints.size(), QgsPoint( 0.0, 0.0 ) ); TiePointInfo tmpInfo; tmpInfo.mLength = std::numeric_limits<double>::infinity(); QVector< TiePointInfo > pointLengthMap( additionalPoints.size(), tmpInfo ); QVector< TiePointInfo >::iterator pointLengthIt; //Graph's points; QVector< QgsPoint > points; QgsFeatureIterator fit = vl->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) ); // begin: tie points to the graph QgsAttributeList la; QgsFeature feature; while ( fit.nextFeature( feature ) ) { QgsMultiPolyline mpl; if ( QgsWkbTypes::flatType( feature.geometry().geometry()->wkbType() ) == QgsWkbTypes::MultiLineString ) mpl = feature.geometry().asMultiPolyline(); else if ( QgsWkbTypes::flatType( feature.geometry().geometry()->wkbType() ) == QgsWkbTypes::LineString ) mpl.push_back( feature.geometry().asPolyline() ); QgsMultiPolyline::iterator mplIt; for ( mplIt = mpl.begin(); mplIt != mpl.end(); ++mplIt ) { QgsPoint pt1, pt2; bool isFirstPoint = true; QgsPolyline::iterator pointIt; for ( pointIt = mplIt->begin(); pointIt != mplIt->end(); ++pointIt ) { pt2 = ct.transform( *pointIt ); points.push_back( pt2 ); if ( !isFirstPoint ) { int i = 0; for ( i = 0; i != additionalPoints.size(); ++i ) { TiePointInfo info; if ( pt1 == pt2 ) { info.mLength = additionalPoints[ i ].sqrDist( pt1 ); info.mTiedPoint = pt1; } else { info.mLength = additionalPoints[ i ].sqrDistToSegment( pt1.x(), pt1.y(), pt2.x(), pt2.y(), info.mTiedPoint ); } if ( pointLengthMap[ i ].mLength > info.mLength ) { Q_UNUSED( info.mTiedPoint ); info.mFirstPoint = pt1; info.mLastPoint = pt2; pointLengthMap[ i ] = info; tiedPoint[ i ] = info.mTiedPoint; } } } pt1 = pt2; isFirstPoint = false; } } emit buildProgress( ++step, featureCount ); } // end: tie points to graph // add tied point to graph int i = 0; for ( i = 0; i < tiedPoint.size(); ++i ) { if ( tiedPoint[ i ] != QgsPoint( 0.0, 0.0 ) ) { points.push_back( tiedPoint [ i ] ); } } QgsPointCompare pointCompare( builder->topologyTolerance() ); qSort( points.begin(), points.end(), pointCompare ); QVector< QgsPoint >::iterator tmp = std::unique( points.begin(), points.end() ); points.resize( tmp - points.begin() ); for ( i = 0;i < points.size();++i ) builder->addVertex( i, points[ i ] ); for ( i = 0; i < tiedPoint.size() ; ++i ) tiedPoint[ i ] = *( my_binary_search( points.begin(), points.end(), tiedPoint[ i ], pointCompare ) ); qSort( pointLengthMap.begin(), pointLengthMap.end(), TiePointInfoCompare ); { // fill attribute list 'la' QgsAttributeList tmpAttr; if ( mDirectionFieldId != -1 ) { tmpAttr.push_back( mDirectionFieldId ); } QList< QgsArcProperter* >::const_iterator it; QgsAttributeList::const_iterator it2; for ( it = mProperterList.begin(); it != mProperterList.end(); ++it ) { QgsAttributeList tmp = ( *it )->requiredAttributes(); for ( it2 = tmp.begin(); it2 != tmp.end(); ++it2 ) { tmpAttr.push_back( *it2 ); } } qSort( tmpAttr.begin(), tmpAttr.end() ); int lastAttrId = -1; for ( it2 = tmpAttr.begin(); it2 != tmpAttr.end(); ++it2 ) { if ( *it2 == lastAttrId ) { continue; } la.push_back( *it2 ); lastAttrId = *it2; } } // end fill attribute list 'la' // begin graph construction fit = vl->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( la ) ); while ( fit.nextFeature( feature ) ) { int directionType = mDefaultDirection; // What direction have feature? QString str = feature.attribute( mDirectionFieldId ).toString(); if ( str == mBothDirectionValue ) { directionType = 3; } else if ( str == mDirectDirectionValue ) { directionType = 1; } else if ( str == mReverseDirectionValue ) { directionType = 2; } // begin features segments and add arc to the Graph; QgsMultiPolyline mpl; if ( QgsWkbTypes::flatType( feature.geometry().geometry()->wkbType() ) == QgsWkbTypes::MultiLineString ) mpl = feature.geometry().asMultiPolyline(); else if ( QgsWkbTypes::flatType( feature.geometry().geometry()->wkbType() ) == QgsWkbTypes::LineString ) mpl.push_back( feature.geometry().asPolyline() ); QgsMultiPolyline::iterator mplIt; for ( mplIt = mpl.begin(); mplIt != mpl.end(); ++mplIt ) { QgsPoint pt1, pt2; bool isFirstPoint = true; QgsPolyline::iterator pointIt; for ( pointIt = mplIt->begin(); pointIt != mplIt->end(); ++pointIt ) { pt2 = ct.transform( *pointIt ); if ( !isFirstPoint ) { QMap< double, QgsPoint > pointsOnArc; pointsOnArc[ 0.0 ] = pt1; pointsOnArc[ pt1.sqrDist( pt2 )] = pt2; TiePointInfo t; t.mFirstPoint = pt1; t.mLastPoint = pt2; t.mLength = 0.0; pointLengthIt = my_binary_search( pointLengthMap.begin(), pointLengthMap.end(), t, TiePointInfoCompare ); if ( pointLengthIt != pointLengthMap.end() ) { QVector< TiePointInfo >::iterator it; for ( it = pointLengthIt; it - pointLengthMap.begin() >= 0; --it ) { if ( it->mFirstPoint == pt1 && it->mLastPoint == pt2 ) { pointsOnArc[ pt1.sqrDist( it->mTiedPoint )] = it->mTiedPoint; } } for ( it = pointLengthIt + 1; it != pointLengthMap.end(); ++it ) { if ( it->mFirstPoint == pt1 && it->mLastPoint == pt2 ) { pointsOnArc[ pt1.sqrDist( it->mTiedPoint )] = it->mTiedPoint; } } } QMap< double, QgsPoint >::iterator pointsIt; QgsPoint pt1; QgsPoint pt2; int pt1idx = -1, pt2idx = -1; bool isFirstPoint = true; for ( pointsIt = pointsOnArc.begin(); pointsIt != pointsOnArc.end(); ++pointsIt ) { pt2 = *pointsIt; tmp = my_binary_search( points.begin(), points.end(), pt2, pointCompare ); pt2 = *tmp; pt2idx = tmp - points.begin(); if ( !isFirstPoint && pt1 != pt2 ) { double distance = builder->distanceArea()->measureLine( pt1, pt2 ); QVector< QVariant > prop; QList< QgsArcProperter* >::const_iterator it; for ( it = mProperterList.begin(); it != mProperterList.end(); ++it ) { prop.push_back(( *it )->property( distance, feature ) ); } if ( directionType == 1 || directionType == 3 ) { builder->addArc( pt1idx, pt1, pt2idx, pt2, prop ); } if ( directionType == 2 || directionType == 3 ) { builder->addArc( pt2idx, pt2, pt1idx, pt1, prop ); } } pt1idx = pt2idx; pt1 = pt2; isFirstPoint = false; } } // if ( !isFirstPoint ) pt1 = pt2; isFirstPoint = false; } // for (it = pl.begin(); it != pl.end(); ++it) } emit buildProgress( ++step, featureCount ); } // while( vl->nextFeature(feature) ) } // makeGraph( QgsGraphBuilderInterface *builder, const QVector< QgsPoint >& additionalPoints, QVector< QgsPoint >& tiedPoint )