QgsGeometry* QgsDelimitedTextFeatureIterator::loadGeometryWkt( const QStringList& tokens, bool &isNull ) { QgsGeometry* geom = nullptr; QString sWkt = tokens[mSource->mWktFieldIndex]; if ( sWkt.isEmpty() ) { isNull = true; return nullptr; } isNull = false; geom = QgsDelimitedTextProvider::geomFromWkt( sWkt, mSource->mWktHasPrefix ); if ( geom && geom->type() != mSource->mGeometryType ) { delete geom; geom = nullptr; } if ( geom && ! wantGeometry( geom ) ) { delete geom; geom = nullptr; } return geom; }
void QgsMapToolLabel::createRubberBands() { delete mLabelRubberBand; delete mFeatureRubberBand; //label rubber band QgsRectangle rect = mCurrentLabel.pos.labelRect; mLabelRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::LineGeometry ); mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMaximum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMaximum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMinimum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) ); mLabelRubberBand->setColor( QColor( 0, 255, 0, 65 ) ); mLabelRubberBand->setWidth( 3 ); mLabelRubberBand->show(); //feature rubber band QgsVectorLayer* vlayer = mCurrentLabel.layer; if ( vlayer ) { QgsFeature f; if ( currentFeature( f, true ) ) { QgsGeometry geom = f.geometry(); if ( !geom.isEmpty() ) { QSettings settings; int r = settings.value( QStringLiteral( "/qgis/digitizing/line_color_red" ), 255 ).toInt(); int g = settings.value( QStringLiteral( "/qgis/digitizing/line_color_green" ), 0 ).toInt(); int b = settings.value( QStringLiteral( "/qgis/digitizing/line_color_blue" ), 0 ).toInt(); int a = settings.value( QStringLiteral( "/qgis/digitizing/line_color_alpha" ), 200 ).toInt(); mFeatureRubberBand = new QgsRubberBand( mCanvas, geom.type() ); mFeatureRubberBand->setColor( QColor( r, g, b, a ) ); mFeatureRubberBand->setToGeometry( geom, vlayer ); mFeatureRubberBand->show(); } } //fixpoint rubber band QgsPoint fixPoint; if ( currentLabelRotationPoint( fixPoint, false, false ) ) { if ( mCanvas ) { const QgsMapSettings& s = mCanvas->mapSettings(); if ( s.hasCrsTransformEnabled() ) { fixPoint = s.mapToLayerCoordinates( vlayer, fixPoint ); } } QgsGeometry pointGeom = QgsGeometry::fromPoint( fixPoint ); mFixPointRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::LineGeometry ); mFixPointRubberBand->setColor( QColor( 0, 0, 255, 65 ) ); mFixPointRubberBand->setToGeometry( pointGeom, vlayer ); mFixPointRubberBand->show(); } } }
void QgsVectorLayerFeatureIterator::useAddedFeature( const QgsFeature& src, QgsFeature& f ) { f.setFeatureId( src.id() ); f.setValid( true ); f.setFields( &L->mUpdatedFields ); if ( src.geometry() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) ) { f.setGeometry( *src.geometry() ); // simplify the edited geometry using its simplifier configured if ( mEditGeometrySimplifier ) { QgsGeometry* geometry = f.geometry(); QGis::GeometryType geometryType = geometry->type(); if ( geometryType == QGis::Line || geometryType == QGis::Polygon ) mEditGeometrySimplifier->simplifyGeometry( geometry ); } } // TODO[MD]: if subset set just some attributes f.setAttributes( src.attributes() ); if ( !mFetchJoinInfo.isEmpty() ) addJoinedAttributes( f ); }
void QgsMapToolLabel::createRubberBands( ) { delete mLabelRubberBand; delete mFeatureRubberBand; //label rubber band QgsRectangle rect = mCurrentLabelPos.labelRect; mLabelRubberBand = new QgsRubberBand( mCanvas, false ); mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMaximum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMaximum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMinimum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) ); mLabelRubberBand->setColor( Qt::green ); mLabelRubberBand->setWidth( 3 ); mLabelRubberBand->show(); //feature rubber band QgsVectorLayer* vlayer = currentLayer(); if ( vlayer ) { QgsFeature f; if ( currentFeature( f, true ) ) { QgsGeometry* geom = f.geometry(); if ( geom ) { mFeatureRubberBand = new QgsRubberBand( mCanvas, geom->type() == QGis::Polygon ); mFeatureRubberBand->setColor( Qt::red ); mFeatureRubberBand->setToGeometry( geom, vlayer ); mFeatureRubberBand->show(); } } //fixpoint rubber band QgsPoint fixPoint; if ( rotationPoint( fixPoint ) ) { if ( mCanvas ) { QgsMapRenderer* r = mCanvas->mapRenderer(); if ( r && r->hasCrsTransformEnabled() ) { fixPoint = r->mapToLayerCoordinates( vlayer, fixPoint ); } } QgsGeometry* pointGeom = QgsGeometry::fromPoint( fixPoint ); mFixPointRubberBand = new QgsRubberBand( mCanvas, false ); mFixPointRubberBand->setColor( Qt::blue ); mFixPointRubberBand->setToGeometry( pointGeom, vlayer ); mFixPointRubberBand->show(); delete pointGeom; } } }
void QgsMapToolLabel::createRubberBands( ) { delete mLabelRubberBand; delete mFeatureRubberBand; //label rubber band QgsRectangle rect = mCurrentLabelPos.labelRect; mLabelRubberBand = new QgsRubberBand( mCanvas, QGis::Line ); mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMaximum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMaximum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMinimum() ) ); mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) ); mLabelRubberBand->setColor( QColor( 0, 255, 0, 65 ) ); mLabelRubberBand->setWidth( 3 ); mLabelRubberBand->show(); //feature rubber band QgsVectorLayer* vlayer = currentLayer(); if ( vlayer ) { QgsFeature f; if ( currentFeature( f, true ) ) { QgsGeometry* geom = f.geometry(); if ( geom ) { mFeatureRubberBand = new QgsRubberBand( mCanvas, geom->type() ); mFeatureRubberBand->setColor( QColor( 255, 0, 0, 65 ) ); mFeatureRubberBand->setToGeometry( geom, vlayer ); mFeatureRubberBand->show(); } } //fixpoint rubber band QgsPoint fixPoint; if ( rotationPoint( fixPoint, false, false ) ) { if ( mCanvas ) { const QgsMapSettings& s = mCanvas->mapSettings(); if ( s.hasCrsTransformEnabled() ) { fixPoint = s.mapToLayerCoordinates( vlayer, fixPoint ); } } QgsGeometry* pointGeom = QgsGeometry::fromPoint( fixPoint ); mFixPointRubberBand = new QgsRubberBand( mCanvas, QGis::Line ); mFixPointRubberBand->setColor( QColor( 0, 0, 255, 65 ) ); mFixPointRubberBand->setToGeometry( pointGeom, vlayer ); mFixPointRubberBand->show(); delete pointGeom; } } }
QVector<QgsPoint> QgsMapToolSimplify::getPointList( QgsFeature& f ) { QgsGeometry* line = f.geometry(); if (( line->type() != QGis::Line && line->type() != QGis::Polygon ) || line->isMultipart() ) { return QVector<QgsPoint>(); } if (( line->type() == QGis::Line ) ) { return line->asPolyline(); } else { if ( line->asPolygon().size() > 1 ) { return QVector<QgsPoint>(); } return line->asPolygon()[0]; } }
void QgsUndoCommand::GeometryChangeEntry::setTargetGeometry( QgsGeometry& dest ) { if ( dest.type() != QGis::UnknownGeometry ) { target = new QgsGeometry( dest ); } else { target = NULL; } }
void QgsRubberBand::setToGeometry( const QgsGeometry& geom, QgsVectorLayer* layer ) { if ( geom.isEmpty() ) { reset( mGeometryType ); return; } reset( geom.type() ); addGeometry( geom, layer ); }
void QgsUndoCommand::GeometryChangeEntry::setOriginalGeometry( QgsGeometry& orig ) { if ( orig.type() != QGis::UnknownGeometry ) { original = new QgsGeometry( orig ); } else { original = NULL; } }
bool QgsSimplifyFeature::simplifyLine( QgsFeature& lineFeature, double tolerance ) { QgsGeometry* line = lineFeature.geometry(); if ( line->type() != QGis::Line ) { return false; } QVector<QgsPoint> resultPoints = simplifyPoints( line->asPolyline(), tolerance ); lineFeature.setGeometry( QgsGeometry::fromPolyline( resultPoints ) ); return true; }
bool QgsSimplifyFeature::simplifyPolygon( QgsFeature& polygonFeature, double tolerance ) { QgsGeometry* polygon = polygonFeature.geometry(); if ( polygon->type() != QGis::Polygon ) { return false; } QVector<QgsPoint> resultPoints = simplifyPoints( polygon->asPolygon()[0], tolerance ); //resultPoints.push_back(resultPoints[0]); QVector<QgsPolyline> poly; poly.append( resultPoints ); polygonFeature.setGeometry( QgsGeometry::fromPolygon( poly ) ); return true; }
void QgsEllipseSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const { double offsetX = 0; double offsetY = 0; markerOffset( context, scaledWidth, scaledHeight, mSymbolWidthUnit, mSymbolHeightUnit, offsetX, offsetY, mSymbolWidthMapUnitScale, mSymbolHeightMapUnitScale ); offset = QPointF( offsetX, offsetY ); //priority for rotation: 1. data defined symbol level, 2. symbol layer rotation (mAngle) bool ok = true; angle = mAngle + mLineAngle; bool usingDataDefinedRotation = false; if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle ) ) { context.setOriginalValueVariable( angle ); angle = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyAngle, context.renderContext().expressionContext(), 0 ) + mLineAngle; usingDataDefinedRotation = ok; } hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || usingDataDefinedRotation; if ( hasDataDefinedRotation ) { // For non-point markers, "dataDefinedRotation" means following the // shape (shape-data defined). For them, "field-data defined" does // not work at all. TODO: if "field-data defined" ever gets implemented // we'll need a way to distinguish here between the two, possibly // using another flag in renderHints() const QgsFeature *f = context.feature(); if ( f ) { const QgsGeometry g = f->geometry(); if ( !g.isNull() && g.type() == QgsWkbTypes::PointGeometry ) { const QgsMapToPixel &m2p = context.renderContext().mapToPixel(); angle += m2p.mapRotation(); } } } if ( angle ) offset = _rotatedOffset( offset, angle ); }
QgsGeometry* QgsDelimitedTextFeatureIterator::loadGeometryWkt( const QStringList& tokens ) { QgsGeometry* geom = 0; QString sWkt = tokens[mSource->mWktFieldIndex]; geom = QgsDelimitedTextProvider::geomFromWkt( sWkt, mSource->mWktHasPrefix, mSource->mWktHasZM ); if ( geom && geom->type() != mSource->mGeometryType ) { delete geom; geom = 0; } if ( geom && ! wantGeometry( geom ) ) { delete geom; geom = 0; } return geom; }
void QgsVectorLayerFeatureIterator::useChangedAttributeFeature( QgsFeatureId fid, const QgsGeometry& geom, QgsFeature& f ) { f.setFeatureId( fid ); f.setValid( true ); f.setFields( &L->mUpdatedFields ); if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) ) { f.setGeometry( geom ); // simplify the edited geometry using its simplifier configured if ( mEditGeometrySimplifier ) { QgsGeometry* geometry = f.geometry(); QGis::GeometryType geometryType = geometry->type(); if ( geometryType == QGis::Line || geometryType == QGis::Polygon ) mEditGeometrySimplifier->simplifyGeometry( geometry ); } } bool subsetAttrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ); if ( !subsetAttrs || ( subsetAttrs && mRequest.subsetOfAttributes().count() > 0 ) ) { // retrieve attributes from provider QgsFeature tmp; //mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes ); QgsFeatureRequest request; request.setFilterFid( fid ).setFlags( QgsFeatureRequest::NoGeometry ); if ( subsetAttrs ) { request.setSubsetOfAttributes( mProviderRequest.subsetOfAttributes() ); } QgsFeatureIterator fi = L->dataProvider()->getFeatures( request ); if ( fi.nextFeature( tmp ) ) { updateChangedAttributes( tmp ); f.setAttributes( tmp.attributes() ); } } if ( !mFetchJoinInfo.isEmpty() ) addJoinedAttributes( f ); }
QList<double> QgsGeometryAnalyzer::simpleMeasure( QgsGeometry& mpGeometry ) { QList<double> list; double perim; if ( mpGeometry.wkbType() == QgsWkbTypes::Point ) { QgsPoint pt = mpGeometry.asPoint(); list.append( pt.x() ); list.append( pt.y() ); } else { QgsDistanceArea measure; list.append( measure.measureArea( mpGeometry ) ); if ( mpGeometry.type() == QgsWkbTypes::PolygonGeometry ) { perim = perimeterMeasure( mpGeometry, measure ); list.append( perim ); } } return list; }
QgsGeometry QgsGeometryAnalyzer::createOffsetGeometry( const QgsGeometry& geom, const QgsGeometry& lineGeom, double offset ) { if ( !geom || lineGeom.isEmpty() ) { return QgsGeometry(); } QList<QgsGeometry> inputGeomList; if ( geom.isMultipart() ) { inputGeomList = geom.asGeometryCollection(); } else { inputGeomList.push_back( geom ); } QList<GEOSGeometry*> outputGeomList; QList<QgsGeometry>::const_iterator inputGeomIt = inputGeomList.constBegin(); GEOSContextHandle_t geosctxt = QgsGeometry::getGEOSHandler(); for ( ; inputGeomIt != inputGeomList.constEnd(); ++inputGeomIt ) { if ( geom.type() == QgsWkbTypes::LineGeometry ) { GEOSGeometry* inputGeomItGeos = inputGeomIt->exportToGeos(); GEOSGeometry* offsetGeom = GEOSOffsetCurve_r( geosctxt, inputGeomItGeos, -offset, 8 /*quadSegments*/, 0 /*joinStyle*/, 5.0 /*mitreLimit*/ ); GEOSGeom_destroy_r( geosctxt, inputGeomItGeos ); if ( !offsetGeom || !GEOSisValid_r( geosctxt, offsetGeom ) ) { return QgsGeometry(); } if ( !GEOSisValid_r( geosctxt, offsetGeom ) || GEOSGeomTypeId_r( geosctxt, offsetGeom ) != GEOS_LINESTRING || GEOSGeomGetNumPoints_r( geosctxt, offsetGeom ) < 1 ) { GEOSGeom_destroy_r( geosctxt, offsetGeom ); return QgsGeometry(); } outputGeomList.push_back( offsetGeom ); } else if ( geom.type() == QgsWkbTypes::PointGeometry ) { QgsPoint p = ( *inputGeomIt ).asPoint(); p = createPointOffset( p.x(), p.y(), offset, lineGeom ); GEOSCoordSequence* ptSeq = GEOSCoordSeq_create_r( geosctxt, 1, 2 ); GEOSCoordSeq_setX_r( geosctxt, ptSeq, 0, p.x() ); GEOSCoordSeq_setY_r( geosctxt, ptSeq, 0, p.y() ); GEOSGeometry* geosPt = GEOSGeom_createPoint_r( geosctxt, ptSeq ); outputGeomList.push_back( geosPt ); } } QgsGeometry outGeometry; if ( !geom.isMultipart() ) { GEOSGeometry* outputGeom = outputGeomList.at( 0 ); if ( outputGeom ) { outGeometry.fromGeos( outputGeom ); } } else { GEOSGeometry** geomArray = new GEOSGeometry*[outputGeomList.size()]; for ( int i = 0; i < outputGeomList.size(); ++i ) { geomArray[i] = outputGeomList.at( i ); } GEOSGeometry* collection = nullptr; if ( geom.type() == QgsWkbTypes::PointGeometry ) { collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOINT, geomArray, outputGeomList.size() ); } else if ( geom.type() == QgsWkbTypes::LineGeometry ) { collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTILINESTRING, geomArray, outputGeomList.size() ); } outGeometry.fromGeos( collection ); delete[] geomArray; } return outGeometry; }
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 && mSpatialIndex != 0; // 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( "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( "X", mWktFieldName ) ); } if ( mYFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( "Y", mWktFieldName ) ); } } if ( messages.size() > 0 ) { 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; 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 = 0; if ( !mWktHasPrefix && sWkt.indexOf( WktPrefixRegexp ) >= 0 ) mWktHasPrefix = true; if ( !mWktHasZM && sWkt.indexOf( WktZMRegexp ) >= 0 ) mWktHasZM = true; geom = geomFromWkt( sWkt, mWktHasPrefix, mWktHasZM ); if ( geom ) { QGis::WkbType type = geom->wkbType(); if ( type != QGis::WKBNoGeometry ) { if ( mGeometryType == QGis::UnknownGeometry || geom->type() == mGeometryType ) { mGeometryType = geom->type(); if ( mNumberFeatures == 0 ) { mNumberFeatures++; mWkbType = type; mExtent = geom->boundingBox(); } else { mNumberFeatures++; if ( geom->isMultipart() ) mWkbType = type; QgsRectangle bbox( geom->boundingBox() ); mExtent.combineExtentWith( &bbox ); } if ( buildSpatialIndex ) { QgsFeature f; f.setFeatureId( mFile->recordId() ); f.setGeometry( geom ); mSpatialIndex->insertFeature( f ); // Feature now has ownership of geometry, so set to null // here to avoid deleting twice. geom = 0; } } else { nIncompatibleGeometry++; geomValid = false; } } if ( geom ) delete geom; } 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 { QgsPoint pt; bool ok = pointFromXY( sX, sY, pt, mDecimalPoint, mXyDms ); if ( ok ) { if ( mNumberFeatures > 0 ) { 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 = QGis::WKBPoint; mGeometryType = QGis::Point; } mNumberFeatures++; if ( buildSpatialIndex && qIsFinite( pt.x() ) && qIsFinite( pt.y() ) ) { QgsFeature f; f.setFeatureId( mFile->recordId() ); f.setGeometry( QgsGeometry::fromPoint( pt ) ); mSpatialIndex->insertFeature( f ); } } else { geomValid = false; nInvalidGeometry++; recordInvalidLine( tr( "Invalid X or Y fields at line %1" ) ); } } } else { mWkbType = QGis::WKBNoGeometry; 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 colum 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; } // 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, "." ); } 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 = "text"; if ( i < csvtTypes.size() ) { if ( csvtTypes[i] == "integer" ) { fieldType = QVariant::Int; typeName = "integer"; } else if ( csvtTypes[i] == "long" || csvtTypes[i] == "longlong" || csvtTypes[i] == "int8" ) { fieldType = QVariant::LongLong; //QVariant doesn't support long typeName = "longlong"; } else if ( csvtTypes[i] == "real" || csvtTypes[i] == "double" ) { fieldType = QVariant::Double; typeName = "double"; } } else if ( i < couldBeInt.size() ) { if ( couldBeInt[i] ) { fieldType = QVariant::Int; typeName = "integer"; } else if ( couldBeLongLong[i] ) { fieldType = QVariant::LongLong; typeName = "longlong"; } else if ( couldBeDouble[i] ) { fieldType = QVariant::Double; typeName = "double"; } } 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 != QGis::UnknownGeometry; mLayerValid = mValid; // If it is valid, then watch for changes to the file connect( mFile, SIGNAL( fileUpdated() ), this, SLOT( onFileUpdated() ) ); }
void checkDock::errorListClicked(const QModelIndex& index) { int row = index.row(); QgsRectangle r = mErrorList[row]->boundingBox(); r.scale(1.5); mQgisApp->mapCanvas()->setExtent(r); mQgisApp->mapCanvas()->refresh(); mFixBox->clear(); mFixBox->addItems(mErrorList[row]->fixNames()); mFixBox->setCurrentIndex(mFixBox->findText("Select automatic fix")); QgsFeature f; QgsGeometry* g; FeatureLayer fl = mErrorList[row]->featurePairs().first(); if (!fl.layer) { std::cout << "invalid layer 1\n"; return; } fl.layer->featureAtId(fl.feature.id(), f, true, false); g = f.geometry(); if (!g) { std::cout << "invalid geometry 1\n"<<std::flush; QMessageBox::information(this, "Topology test", "Feature not found in the layer.\nThe layer has probably changed.\nRun topology check again."); return; } clearVertexMarkers(); // use vertex marker when highlighting a point // and rubber band otherwise if (g->type() == QGis::Point) { mVMFeature1 = new QgsVertexMarker(mQgisApp->mapCanvas()); mVMFeature1->setIconType(QgsVertexMarker::ICON_BOX); mVMConflict->setPenWidth(5); mVMFeature1->setIconSize(5); mVMFeature1->setColor("blue"); mVMFeature1->setCenter(g->asPoint()); } else mRBFeature1->setToGeometry(g, fl.layer); fl = mErrorList[row]->featurePairs()[1]; if (!fl.layer) { std::cout << "invalid layer 2\n"; return; } fl.layer->featureAtId(fl.feature.id(), f, true, false); g = f.geometry(); if (!g) { std::cout << "invalid geometry 2\n" << std::flush; QMessageBox::information(this, "Topology test", "Feature not found in the layer.\nThe layer has probably changed.\nRun topology check again."); return; } if (g->type() == QGis::Point) { mVMFeature2 = new QgsVertexMarker(mQgisApp->mapCanvas()); mVMFeature2->setIconType(QgsVertexMarker::ICON_BOX); mVMConflict->setPenWidth(5); mVMConflict->setIconSize(5); mVMFeature2->setColor("red"); mVMFeature2->setCenter(g->asPoint()); } else mRBFeature2->setToGeometry(g, fl.layer); if (!mErrorList[row]->conflict()) { std::cout << "invalid conflict\n" << std::flush; return; } if (mErrorList[row]->conflict()->type() == QGis::Point) { mVMConflict = new QgsVertexMarker(mQgisApp->mapCanvas()); mVMConflict->setIconType(QgsVertexMarker::ICON_BOX); mVMConflict->setPenWidth(5); mVMConflict->setIconSize(5); mVMConflict->setColor("gold"); mVMConflict->setCenter(mErrorList[row]->conflict()->asPoint()); } else mRBConflict->setToGeometry(mErrorList[row]->conflict(), fl.layer); }
int QgsMapCanvasSnapper::snapToBackgroundLayers( const QgsPoint& point, QList<QgsSnappingResult>& results, const QList<QgsPoint>& excludePoints ) { results.clear(); if ( !mSnapper ) return 5; //topological editing on? int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); //snapping on intersection on? int intersectionSnapping = QgsProject::instance()->readNumEntry( "Digitizing", "/IntersectionSnapping", 0 ); if ( topologicalEditing == 0 ) { if ( intersectionSnapping == 0 ) mSnapper->setSnapMode( QgsSnapper::SnapWithOneResult ); else mSnapper->setSnapMode( QgsSnapper::SnapWithResultsWithinTolerances ); } else if ( intersectionSnapping == 0 ) { mSnapper->setSnapMode( QgsSnapper::SnapWithResultsForSamePosition ); } else { mSnapper->setSnapMode( QgsSnapper::SnapWithResultsWithinTolerances ); } //read snapping settings from project bool snappingDefinedInProject, ok; QStringList layerIdList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingList", QStringList(), &snappingDefinedInProject ); QStringList enabledList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingEnabledList", QStringList(), &ok ); QStringList toleranceList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceList", QStringList(), &ok ); QStringList toleranceUnitList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList", QStringList(), &ok ); QStringList snapToList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnapToList", QStringList(), &ok ); if ( !( layerIdList.size() == enabledList.size() && layerIdList.size() == toleranceList.size() && layerIdList.size() == toleranceUnitList.size() && layerIdList.size() == snapToList.size() ) ) { // lists must have the same size, otherwise something is wrong return 1; } QList<QgsSnapper::SnapLayer> snapLayers; QgsSnapper::SnapLayer snapLayer; // Use snapping information from the project if ( snappingDefinedInProject ) { // set layers, tolerances, snap to segment/vertex to QgsSnapper QStringList::const_iterator layerIt( layerIdList.constBegin() ); QStringList::const_iterator tolIt( toleranceList.constBegin() ); QStringList::const_iterator tolUnitIt( toleranceUnitList.constBegin() ); QStringList::const_iterator snapIt( snapToList.constBegin() ); QStringList::const_iterator enabledIt( enabledList.constBegin() ); for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt ) { if ( *enabledIt != "enabled" ) { // skip layer if snapping is not enabled continue; } //layer QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( *layerIt ) ); if ( !vlayer || !vlayer->hasGeometryType() ) continue; snapLayer.mLayer = vlayer; //tolerance snapLayer.mTolerance = tolIt->toDouble(); snapLayer.mUnitType = ( QgsTolerance::UnitType ) tolUnitIt->toInt(); // segment or vertex if ( *snapIt == "to_vertex" ) { snapLayer.mSnapTo = QgsSnapper::SnapToVertex; } else if ( *snapIt == "to_segment" ) { snapLayer.mSnapTo = QgsSnapper::SnapToSegment; } else { // to vertex and segment snapLayer.mSnapTo = QgsSnapper::SnapToVertexAndSegment; } snapLayers.append( snapLayer ); } } else { // nothing in project. Use default snapping tolerance to vertex of current layer QgsMapLayer* currentLayer = mMapCanvas->currentLayer(); if ( !currentLayer ) return 2; QgsVectorLayer* currentVectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer ); if ( !currentVectorLayer ) return 3; snapLayer.mLayer = currentVectorLayer; //default snap mode QSettings settings; QString defaultSnapString = settings.value( "/qgis/digitizing/default_snap_mode", "off" ).toString(); if ( defaultSnapString == "to segment" ) { snapLayer.mSnapTo = QgsSnapper::SnapToSegment; } else if ( defaultSnapString == "to vertex and segment" ) { snapLayer.mSnapTo = QgsSnapper::SnapToVertexAndSegment; } else if ( defaultSnapString == "to vertex" ) { snapLayer.mSnapTo = QgsSnapper::SnapToVertex; } else { return 0; } //default snapping tolerance (returned in map units) snapLayer.mTolerance = QgsTolerance::defaultTolerance( currentVectorLayer, mMapCanvas->mapSettings() ); snapLayer.mUnitType = QgsTolerance::LayerUnits; snapLayers.append( snapLayer ); } mSnapper->setSnapLayers( snapLayers ); if ( mSnapper->snapMapPoint( point, results, excludePoints ) != 0 ) return 4; if ( intersectionSnapping != 1 ) return 0; QList<QgsSnappingResult> segments; QList<QgsSnappingResult> points; for ( QList<QgsSnappingResult>::const_iterator it = results.constBegin(); it != results.constEnd(); ++it ) { if ( it->snappedVertexNr == -1 ) { QgsDebugMsg( "segment" ); segments.push_back( *it ); } else { QgsDebugMsg( "no segment" ); points.push_back( *it ); } } if ( segments.length() < 2 ) return 0; QList<QgsSnappingResult> myResults; for ( QList<QgsSnappingResult>::const_iterator oSegIt = segments.constBegin(); oSegIt != segments.constEnd(); ++oSegIt ) { QgsDebugMsg( QString::number( oSegIt->beforeVertexNr ) ); QVector<QgsPoint> vertexPoints; vertexPoints.append( oSegIt->beforeVertex ); vertexPoints.append( oSegIt->afterVertex ); QgsGeometry* lineA = QgsGeometry::fromPolyline( vertexPoints ); for ( QList<QgsSnappingResult>::iterator iSegIt = segments.begin(); iSegIt != segments.end(); ++iSegIt ) { QVector<QgsPoint> vertexPoints; vertexPoints.append( iSegIt->beforeVertex ); vertexPoints.append( iSegIt->afterVertex ); QgsGeometry* lineB = QgsGeometry::fromPolyline( vertexPoints ); QgsGeometry* intersectionPoint = lineA->intersection( lineB ); delete lineB; if ( intersectionPoint && intersectionPoint->type() == QGis::Point ) { //We have to check the intersection point is inside the tolerance distance for both layers double toleranceA = 0; double toleranceB = 0; for ( int i = 0 ;i < snapLayers.size();++i ) { if ( snapLayers[i].mLayer == oSegIt->layer ) { toleranceA = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType ); } if ( snapLayers[i].mLayer == iSegIt->layer ) { toleranceB = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType ); } } QgsGeometry* cursorPoint = QgsGeometry::fromPoint( point ); double distance = intersectionPoint->distance( *cursorPoint ); if ( distance < toleranceA && distance < toleranceB ) { iSegIt->snappedVertex = intersectionPoint->asPoint(); myResults.append( *iSegIt ); } delete cursorPoint; } delete intersectionPoint; } delete lineA; } if ( myResults.length() > 0 ) { results.clear(); results = myResults; } return 0; }
QVariantMap QgsLineIntersectionAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsFeatureSource > sourceA( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !sourceA ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); std::unique_ptr< QgsFeatureSource > sourceB( parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context ) ); if ( !sourceB ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INTERSECT" ) ) ); const QStringList fieldsA = parameterAsFields( parameters, QStringLiteral( "INPUT_FIELDS" ), context ); const QStringList fieldsB = parameterAsFields( parameters, QStringLiteral( "INTERSECT_FIELDS" ), context ); QgsAttributeList fieldIndicesA = QgsProcessingUtils::fieldNamesToIndices( fieldsA, sourceA->fields() ); QgsAttributeList fieldIndicesB = QgsProcessingUtils::fieldNamesToIndices( fieldsB, sourceB->fields() ); QString intersectFieldsPrefix = parameterAsString( parameters, QStringLiteral( "INTERSECT_FIELDS_PREFIX" ), context ); QgsFields outFields = QgsProcessingUtils::combineFields( QgsProcessingUtils::indicesToFields( fieldIndicesA, sourceA->fields() ), QgsProcessingUtils::indicesToFields( fieldIndicesB, sourceB->fields() ), intersectFieldsPrefix ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outFields, QgsWkbTypes::Point, sourceA->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); QgsSpatialIndex spatialIndex( sourceB->getFeatures( QgsFeatureRequest().setNoAttributes().setDestinationCrs( sourceA->sourceCrs(), context.transformContext() ) ), feedback ); QgsFeature outFeature; QgsFeatureIterator features = sourceA->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( fieldIndicesA ) ); double step = sourceA->featureCount() > 0 ? 100.0 / sourceA->featureCount() : 1; int i = 0; QgsFeature inFeatureA; while ( features.nextFeature( inFeatureA ) ) { i++; if ( feedback->isCanceled() ) { break; } if ( !inFeatureA.hasGeometry() ) continue; QgsGeometry inGeom = inFeatureA.geometry(); QgsFeatureIds lines = spatialIndex.intersects( inGeom.boundingBox() ).toSet(); if ( !lines.empty() ) { // use prepared geometries for faster intersection tests std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( inGeom.constGet() ) ); engine->prepareGeometry(); QgsFeatureRequest request = QgsFeatureRequest().setFilterFids( lines ); request.setDestinationCrs( sourceA->sourceCrs(), context.transformContext() ); request.setSubsetOfAttributes( fieldIndicesB ); QgsFeature inFeatureB; QgsFeatureIterator featuresB = sourceB->getFeatures( request ); while ( featuresB.nextFeature( inFeatureB ) ) { if ( feedback->isCanceled() ) { break; } QgsGeometry tmpGeom = inFeatureB.geometry(); if ( engine->intersects( tmpGeom.constGet() ) ) { QgsMultiPointXY points; QgsGeometry intersectGeom = inGeom.intersection( tmpGeom ); QgsAttributes outAttributes; for ( int a : qgis::as_const( fieldIndicesA ) ) { outAttributes.append( inFeatureA.attribute( a ) ); } for ( int b : qgis::as_const( fieldIndicesB ) ) { outAttributes.append( inFeatureB.attribute( b ) ); } if ( QgsWkbTypes::flatType( intersectGeom.wkbType() ) == QgsWkbTypes::GeometryCollection ) { const QVector<QgsGeometry> geomCollection = intersectGeom.asGeometryCollection(); for ( const QgsGeometry &part : geomCollection ) { if ( part.type() == QgsWkbTypes::PointGeometry ) { if ( part.isMultipart() ) { points = part.asMultiPoint(); } else { points.append( part.asPoint() ); } } } } else if ( intersectGeom.type() == QgsWkbTypes::PointGeometry ) { if ( intersectGeom.isMultipart() ) { points = intersectGeom.asMultiPoint(); } else { points.append( intersectGeom.asPoint() ); } } for ( const QgsPointXY &j : qgis::as_const( points ) ) { outFeature.setGeometry( QgsGeometry::fromPointXY( j ) ); outFeature.setAttributes( outAttributes ); sink->addFeature( outFeature, QgsFeatureSink::FastInsert ); } } } } feedback->setProgress( i * step ); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }
void checkDock::errorListClicked( const QModelIndex& index ) { int row = index.row(); QgsRectangle r = mErrorList[row]->boundingBox(); r.scale( 1.5 ); QgsMapCanvas* canvas = qgsInterface->mapCanvas(); canvas->setExtent( r ); canvas->refresh(); mFixBox->clear(); mFixBox->addItems( mErrorList[row]->fixNames() ); mFixBox->setCurrentIndex( mFixBox->findText( tr( "Select automatic fix" ) ) ); QgsFeature f; QgsGeometry* g; FeatureLayer fl = mErrorList[row]->featurePairs().first(); if ( !fl.layer ) { QgsMessageLog::logMessage( tr( "Invalid first layer" ), tr( "Topology plugin" ) ); return; } //fl1.layer->getFeatures( QgsFeatureRequest().setFilterFid( fl1.feature.id() ) ).nextFeature( f1 ); fl.layer->getFeatures( QgsFeatureRequest().setFilterFid( fl.feature.id() ) ).nextFeature( f ); g = f.geometry(); if ( !g ) { QgsMessageLog::logMessage( tr( "Invalid first geometry" ), tr( "Topology plugin" ) ); QMessageBox::information( this, tr( "Topology test" ), tr( "Feature not found in the layer.\nThe layer has probably changed.\nRun topology check again." ) ); return; } clearVertexMarkers(); // use vertex marker when highlighting a point // and rubber band otherwise if ( g->type() == QGis::Point ) { mVMFeature1 = new QgsVertexMarker( canvas ); mVMFeature1->setIconType( QgsVertexMarker::ICON_X ); mVMFeature1->setPenWidth( 5 ); mVMFeature1->setIconSize( 5 ); mVMFeature1->setColor( "blue" ); mVMFeature1->setCenter( g->asPoint() ); } else mRBFeature1->setToGeometry( g, fl.layer ); fl = mErrorList[row]->featurePairs()[1]; if ( !fl.layer ) { QgsMessageLog::logMessage( tr( "Invalid second layer" ), tr( "Topology plugin" ) ); return; } fl.layer->getFeatures( QgsFeatureRequest().setFilterFid( fl.feature.id() ) ).nextFeature( f ); g = f.geometry(); if ( !g ) { QgsMessageLog::logMessage( tr( "Invalid second geometry" ), tr( "Topology plugin" ) ); QMessageBox::information( this, tr( "Topology test" ), tr( "Feature not found in the layer.\nThe layer has probably changed.\nRun topology check again." ) ); return; } if ( g->type() == QGis::Point ) { mVMFeature2 = new QgsVertexMarker( canvas ); mVMFeature2->setIconType( QgsVertexMarker::ICON_BOX ); mVMFeature2->setPenWidth( 5 ); mVMFeature2->setIconSize( 5 ); mVMFeature2->setColor( "green" ); mVMFeature2->setCenter( g->asPoint() ); } else mRBFeature2->setToGeometry( g, fl.layer ); if ( !mErrorList[row]->conflict() ) { QgsMessageLog::logMessage( tr( "Invalid conflict" ), tr( "Topology plugin" ) ); return; } if ( mErrorList[row]->conflict()->type() == QGis::Point ) { mVMConflict = new QgsVertexMarker( canvas ); mVMConflict->setIconType( QgsVertexMarker::ICON_BOX ); mVMConflict->setPenWidth( 5 ); mVMConflict->setIconSize( 5 ); mVMConflict->setColor( "red" ); mVMConflict->setCenter( mErrorList[row]->conflict()->asPoint() ); } else mRBConflict->setToGeometry( mErrorList[row]->conflict(), fl.layer ); }
QgsGeometry* QgsTransectSample::clipBufferLine( const QgsGeometry& stratumGeom, QgsGeometry* clippedBaseline, double tolerance ) { if ( !stratumGeom || !clippedBaseline || clippedBaseline->wkbType() == QgsWkbTypes::Unknown ) { return nullptr; } QgsGeometry usedBaseline = *clippedBaseline; if ( mBaselineSimplificationTolerance >= 0 ) { //int verticesBefore = usedBaseline->asMultiPolyline().count(); usedBaseline = clippedBaseline->simplify( mBaselineSimplificationTolerance ); if ( usedBaseline.isEmpty() ) { return nullptr; } //int verticesAfter = usedBaseline->asMultiPolyline().count(); //debug: write to file /*QgsVectorFileWriter debugWriter( "/tmp/debug.shp", "utf-8", QgsFields(), QgsWkbTypes::LineString, &( mStrataLayer->crs() ) ); QgsFeature debugFeature; debugFeature.setGeometry( usedBaseline ); debugWriter.addFeature( debugFeature );*/ } double currentBufferDist = tolerance; int maxLoops = 10; for ( int i = 0; i < maxLoops; ++i ) { //loop with tolerance: create buffer, convert buffer to line, clip line by stratum, test if result is (single) line QgsGeometry clipBaselineBuffer = usedBaseline.buffer( currentBufferDist, 8 ); if ( clipBaselineBuffer.isEmpty() ) { continue; } //it is also possible that clipBaselineBuffer is a multipolygon QgsGeometry bufferLine; //buffer line or multiline QgsGeometry bufferLineClipped; QgsMultiPolyline mpl; if ( clipBaselineBuffer.isMultipart() ) { QgsMultiPolygon bufferMultiPolygon = clipBaselineBuffer.asMultiPolygon(); if ( bufferMultiPolygon.size() < 1 ) { continue; } for ( int j = 0; j < bufferMultiPolygon.size(); ++j ) { int size = bufferMultiPolygon.at( j ).size(); for ( int k = 0; k < size; ++k ) { mpl.append( bufferMultiPolygon.at( j ).at( k ) ); } } bufferLine = QgsGeometry::fromMultiPolyline( mpl ); } else { QgsPolygon bufferPolygon = clipBaselineBuffer.asPolygon(); if ( bufferPolygon.size() < 1 ) { continue; } int size = bufferPolygon.size(); mpl.reserve( size ); for ( int j = 0; j < size; ++j ) { mpl.append( bufferPolygon[j] ); } bufferLine = QgsGeometry::fromMultiPolyline( mpl ); } bufferLineClipped = bufferLine.intersection( stratumGeom ); if ( bufferLineClipped.isEmpty() && bufferLineClipped.type() == QgsWkbTypes::LineGeometry ) { //if stratumGeom is a multipolygon, bufferLineClipped must intersect each part bool bufferLineClippedIntersectsStratum = true; if ( stratumGeom.wkbType() == QgsWkbTypes::MultiPolygon || stratumGeom.wkbType() == QgsWkbTypes::MultiPolygon25D ) { QVector<QgsPolygon> multiPoly = stratumGeom.asMultiPolygon(); QVector<QgsPolygon>::const_iterator multiIt = multiPoly.constBegin(); for ( ; multiIt != multiPoly.constEnd(); ++multiIt ) { QgsGeometry poly = QgsGeometry::fromPolygon( *multiIt ); if ( !poly.intersects( bufferLineClipped ) ) { bufferLineClippedIntersectsStratum = false; break; } } } if ( bufferLineClippedIntersectsStratum ) { return new QgsGeometry( bufferLineClipped ); } } currentBufferDist /= 2; } return nullptr; //no solution found even with reduced tolerances }
QgsFeatureIds QgsMapToolSelectUtils::getMatchingFeatures( QgsMapCanvas *canvas, const QgsGeometry &selectGeometry, bool doContains, bool singleSelect ) { QgsFeatureIds newSelectedFeatures; if ( selectGeometry.type() != QgsWkbTypes::PolygonGeometry ) return newSelectedFeatures; QgsVectorLayer *vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( !vlayer ) return newSelectedFeatures; // 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; try { QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs(), QgsProject::instance() ); if ( !ct.isShortCircuited() && selectGeomTrans.type() == QgsWkbTypes::PolygonGeometry ) { // convert add more points to the edges of the rectangle // improve transformation result QgsPolygonXY poly( selectGeomTrans.asPolygon() ); if ( poly.size() == 1 && poly.at( 0 ).size() == 5 ) { const QgsPolylineXY &ringIn = poly.at( 0 ); QgsPolygonXY newpoly( 1 ); newpoly[0].resize( 41 ); QgsPolylineXY &ringOut = newpoly[0]; ringOut[ 0 ] = ringIn.at( 0 ); int i = 1; for ( int j = 1; j < 5; j++ ) { QgsVector v( ( ringIn.at( j ) - ringIn.at( j - 1 ) ) / 10.0 ); for ( int k = 0; k < 9; k++ ) { ringOut[ i ] = ringOut[ i - 1 ] + v; i++; } ringOut[ i++ ] = ringIn.at( j ); } selectGeomTrans = QgsGeometry::fromPolygonXY( newpoly ); } } selectGeomTrans.transform( ct ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and leave existing selection unchanged QgsDebugMsg( QStringLiteral( "Caught CRS exception " ) ); QgisApp::instance()->messageBar()->pushMessage( QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system" ), Qgis::Warning, QgisApp::instance()->messageTimeout() ); return newSelectedFeatures; } QgsDebugMsgLevel( "Selection layer: " + vlayer->name(), 3 ); QgsDebugMsgLevel( "Selection polygon: " + selectGeomTrans.asWkt(), 3 ); QgsDebugMsgLevel( "doContains: " + QString( doContains ? "T" : "F" ), 3 ); QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() ); context.expressionContext() << QgsExpressionContextUtils::layerScope( vlayer ); std::unique_ptr< QgsFeatureRenderer > r; if ( vlayer->renderer() ) { r.reset( vlayer->renderer()->clone() ); r->startRender( context, vlayer->fields() ); } QgsFeatureRequest request; request.setFilterRect( selectGeomTrans.boundingBox() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); if ( r ) request.setSubsetOfAttributes( r->usedAttributes( context ), vlayer->fields() ); else request.setNoAttributes(); QgsFeatureIterator fit = vlayer->getFeatures( request ); QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { context.expressionContext().setFeature( f ); // make sure to only use features that are visible if ( r && !r->willRenderFeature( f, context ) ) continue; QgsGeometry g = f.geometry(); 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() ) ); return newSelectedFeatures; }
QgsGeometry* QgsTransectSample::clipBufferLine( QgsGeometry* stratumGeom, QgsGeometry* clippedBaseline, double tolerance ) { if ( !stratumGeom || !clippedBaseline || clippedBaseline->wkbType() == QGis::WKBUnknown ) { return 0; } double currentBufferDist = tolerance; int maxLoops = 10; for ( int i = 0; i < maxLoops; ++i ) { //loop with tolerance: create buffer, convert buffer to line, clip line by stratum, test if result is (single) line QgsGeometry* clipBaselineBuffer = clippedBaseline->buffer( currentBufferDist, 8 ); if ( !clipBaselineBuffer ) { delete clipBaselineBuffer; continue; } //it is also possible that clipBaselineBuffer is a multipolygon QgsGeometry* bufferLine = 0; //buffer line or multiline QgsGeometry* bufferLineClipped = 0; QgsMultiPolyline mpl; if ( clipBaselineBuffer->isMultipart() ) { QgsMultiPolygon bufferMultiPolygon = clipBaselineBuffer->asMultiPolygon(); if ( bufferMultiPolygon.size() < 1 ) { delete clipBaselineBuffer; continue; } for ( int j = 0; j < bufferMultiPolygon.size(); ++j ) { int size = bufferMultiPolygon.at( j ).size(); for ( int k = 0; k < size; ++k ) { mpl.append( bufferMultiPolygon.at( j ).at( k ) ); } } bufferLine = QgsGeometry::fromMultiPolyline( mpl ); } else { QgsPolygon bufferPolygon = clipBaselineBuffer->asPolygon(); if ( bufferPolygon.size() < 1 ) { delete clipBaselineBuffer; continue; } int size = bufferPolygon.size(); for ( int j = 0; j < size; ++j ) { mpl.append( bufferPolygon[j] ); } bufferLine = QgsGeometry::fromMultiPolyline( mpl ); } bufferLineClipped = bufferLine->intersection( stratumGeom ); if ( bufferLineClipped && bufferLineClipped->type() == QGis::Line ) { //if stratumGeom is a multipolygon, bufferLineClipped must intersect each part bool bufferLineClippedIntersectsStratum = true; if ( stratumGeom->wkbType() == QGis::WKBMultiPolygon || stratumGeom->wkbType() == QGis::WKBMultiPolygon25D ) { QVector<QgsPolygon> multiPoly = stratumGeom->asMultiPolygon(); QVector<QgsPolygon>::const_iterator multiIt = multiPoly.constBegin(); for ( ; multiIt != multiPoly.constEnd(); ++multiIt ) { QgsGeometry* poly = QgsGeometry::fromPolygon( *multiIt ); if ( !poly->intersects( bufferLineClipped ) ) { bufferLineClippedIntersectsStratum = false; delete poly; break; } delete poly; } } if ( bufferLineClippedIntersectsStratum ) { return bufferLineClipped; } } delete bufferLineClipped; delete clipBaselineBuffer; delete bufferLine; currentBufferDist /= 2; } return 0; //no solution found even with reduced tolerances }