void QgsZonalStatistics::statisticsFromPreciseIntersection( void* band, const QgsGeometry& poly, int pixelOffsetX, int pixelOffsetY, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle& rasterBBox, FeatureStats &stats ) { stats.reset(); double currentY = rasterBBox.yMaximum() - pixelOffsetY * cellSizeY - cellSizeY / 2; float* pixelData = ( float * ) CPLMalloc( sizeof( float ) ); QgsGeometry pixelRectGeometry; double hCellSizeX = cellSizeX / 2.0; double hCellSizeY = cellSizeY / 2.0; double pixelArea = cellSizeX * cellSizeY; double weight = 0; for ( int row = 0; row < nCellsY; ++row ) { double currentX = rasterBBox.xMinimum() + cellSizeX / 2.0 + pixelOffsetX * cellSizeX; for ( int col = 0; col < nCellsX; ++col ) { if ( GDALRasterIO( band, GF_Read, pixelOffsetX + col, pixelOffsetY + row, nCellsX, 1, pixelData, 1, 1, GDT_Float32, 0, 0 ) != CE_None ) { QgsDebugMsg( "Raster IO Error" ); } if ( !validPixel( *pixelData ) ) continue; pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) ); if ( !pixelRectGeometry.isEmpty() ) { //intersection QgsGeometry intersectGeometry = pixelRectGeometry.intersection( poly ); if ( !intersectGeometry.isEmpty() ) { double intersectionArea = intersectGeometry.area(); if ( intersectionArea >= 0.0 ) { weight = intersectionArea / pixelArea; stats.addValue( *pixelData, weight ); } } pixelRectGeometry = QgsGeometry(); } currentX += cellSizeX; } currentY -= cellSizeY; } CPLFree( pixelData ); }
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(); } } }
int QgsInterpolator::addVerticesToCache( const QgsGeometry& geom, bool zCoord, double attributeValue ) { if ( geom.isEmpty() ) return 1; bool hasZValue = false; QByteArray wkb( geom.exportToWkb() ); QgsConstWkbPtr currentWkbPtr( wkb ); currentWkbPtr.readHeader(); vertexData theVertex; //the current vertex QgsWkbTypes::Type wkbType = geom.wkbType(); switch ( wkbType ) { case QgsWkbTypes::Point25D: hasZValue = true; //intentional fall-through FALLTHROUGH; case QgsWkbTypes::Point: { currentWkbPtr >> theVertex.x >> theVertex.y; if ( zCoord && hasZValue ) { currentWkbPtr >> theVertex.z; } else { theVertex.z = attributeValue; } mCachedBaseData.push_back( theVertex ); break; }
bool QgsOverlayUtils::sanitizeIntersectionResult( QgsGeometry &geom, QgsWkbTypes::GeometryType geometryType ) { if ( geom.isNull() ) { // TODO: not sure if this ever happens - if it does, that means GEOS failed badly - would be good to have a test for such situation throw QgsProcessingException( QStringLiteral( "%1\n\n%2" ).arg( QObject::tr( "GEOS geoprocessing error: intersection failed." ), geom.lastError() ) ); } // Intersection of geometries may give use also geometries we do not want in our results. // For example, two square polygons touching at the corner have a point as the intersection, but no area. // In other cases we may get a mixture of geometries in the output - we want to keep only the expected types. if ( QgsWkbTypes::flatType( geom.wkbType() ) == QgsWkbTypes::GeometryCollection ) { // try to filter out irrelevant parts with different geometry type than what we want geom.convertGeometryCollectionToSubclass( geometryType ); if ( geom.isEmpty() ) return false; } if ( QgsWkbTypes::geometryType( geom.wkbType() ) != geometryType ) { // we can't make use of this resulting geometry return false; } // some data providers are picky about the geometries we pass to them: we can't add single-part geometries // when we promised multi-part geometries, so ensure we have the right type geom.convertToMultiType(); return true; }
QgsGeometry QgsGeometryAnalyzer::locateBetweenMeasures( double fromMeasure, double toMeasure, const QgsGeometry& lineGeom ) { if ( lineGeom.isEmpty() ) { return QgsGeometry(); } QgsMultiPolyline resultGeom; //need to go with WKB and z coordinate until QgsGeometry supports M values QByteArray wkb( lineGeom.exportToWkb() ); QgsConstWkbPtr wkbPtr( wkb ); wkbPtr.readHeader(); QgsWkbTypes::Type wkbType = lineGeom.wkbType(); if ( wkbType != QgsWkbTypes::LineString25D && wkbType != QgsWkbTypes::MultiLineString25D ) { return QgsGeometry(); } if ( wkbType == QgsWkbTypes::LineString25D ) { locateBetweenWkbString( wkbPtr, resultGeom, fromMeasure, toMeasure ); } else if ( wkbType == QgsWkbTypes::MultiLineString25D ) { int nLines; wkbPtr >> nLines; for ( int i = 0; i < nLines; ++i ) { wkbPtr.readHeader(); wkbPtr = locateBetweenWkbString( wkbPtr, resultGeom, fromMeasure, toMeasure ); } }
QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertexV2( QgsFeatureId featureId, int vertex ) { if ( !L->hasGeometryType() ) return QgsVectorLayer::InvalidLayer; QgsGeometry geometry; if ( !cache()->geometry( featureId, geometry ) ) { // it's not in cache: let's fetch it from layer QgsFeature f; if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) return QgsVectorLayer::FetchFeatureFailed; // geometry not found geometry = f.geometry(); } if ( !geometry.deleteVertex( vertex ) ) return QgsVectorLayer::EditFailed; if ( geometry.geometry() && geometry.geometry()->nCoordinates() == 0 ) { //last vertex deleted, set geometry to null geometry.setGeometry( nullptr ); } L->editBuffer()->changeGeometry( featureId, geometry ); return !geometry.isEmpty() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry; }
ErrorList topolTest::checkMultipart( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( tolerance ); Q_UNUSED( layer2 ); Q_UNUSED( layer1 ); Q_UNUSED( isExtent ); int i = 0; ErrorList errorList; QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( ++i ); if ( testCancelled() ) break; QgsGeometry g = it->feature.geometry(); if ( g.isEmpty() ) { QgsMessageLog::logMessage( tr( "Missing geometry in multipart check." ), tr( "Topology plugin" ) ); continue; } if ( !_canExportToGeos( g ) ) continue; if ( g.isMultipart() ) { QgsRectangle r = g.boundingBox(); QList<FeatureLayer> fls; fls << *it << *it; TopolErroMultiPart* err = new TopolErroMultiPart( r, g, fls ); errorList << err; } } return errorList; }
double QgsDistanceArea::measureLength( const QgsGeometry& geometry ) const { if ( geometry.isEmpty() ) return 0.0; const QgsAbstractGeometry* geomV2 = geometry.geometry(); return measure( geomV2, Length ); }
void QgsGeometryAnalyzer::addEventLayerFeature( QgsFeature& feature, const QgsGeometry& geom, const QgsGeometry& lineGeom, QgsVectorFileWriter* fileWriter, QgsFeatureList& memoryFeatures, int offsetField, double offsetScale, bool forceSingleType ) { if ( geom.isEmpty() ) { return; } QList<QgsGeometry> geomList; if ( forceSingleType ) { geomList = geom.asGeometryCollection(); } else { geomList.push_back( geom ); } QList<QgsGeometry>::iterator geomIt = geomList.begin(); for ( ; geomIt != geomList.end(); ++geomIt ) { //consider offset QgsGeometry newGeom = *geomIt; if ( offsetField >= 0 ) { double offsetVal = feature.attribute( offsetField ).toDouble(); offsetVal *= offsetScale; newGeom = createOffsetGeometry( *geomIt, lineGeom, offsetVal ); if ( newGeom.isEmpty() ) { continue; } } feature.setGeometry( newGeom ); if ( fileWriter ) { fileWriter->addFeature( feature ); } else { memoryFeatures << feature; } } }
double QgsDistanceArea::measurePerimeter( const QgsGeometry& geometry ) const { if ( geometry.isEmpty() ) return 0.0; const QgsAbstractGeometry* geomV2 = geometry.geometry(); if ( !geomV2 || geomV2->dimension() < 2 ) { return 0.0; } if ( !mEllipsoidalMode || mEllipsoid == GEO_NONE ) { return geomV2->perimeter(); } //create list with (single) surfaces QList< const QgsSurface* > surfaces; const QgsSurface* surf = dynamic_cast<const QgsSurface*>( geomV2 ); if ( surf ) { surfaces.append( surf ); } const QgsMultiSurface* multiSurf = dynamic_cast<const QgsMultiSurface*>( geomV2 ); if ( multiSurf ) { surfaces.reserve(( surf ? 1 : 0 ) + multiSurf->numGeometries() ); for ( int i = 0; i < multiSurf->numGeometries(); ++i ) { surfaces.append( static_cast<const QgsSurface*>( multiSurf->geometryN( i ) ) ); } } double length = 0; QList<const QgsSurface*>::const_iterator surfaceIt = surfaces.constBegin(); for ( ; surfaceIt != surfaces.constEnd(); ++surfaceIt ) { if ( !*surfaceIt ) { continue; } QgsPolygonV2* poly = ( *surfaceIt )->surfaceToPolygon(); const QgsCurve* outerRing = poly->exteriorRing(); if ( outerRing ) { length += measure( outerRing ); } int nInnerRings = poly->numInteriorRings(); for ( int i = 0; i < nInnerRings; ++i ) { length += measure( poly->interiorRing( i ) ); } delete poly; } return length; }
void QgsRubberBand::setToGeometry( const QgsGeometry& geom, QgsVectorLayer* layer ) { if ( geom.isEmpty() ) { reset( mGeometryType ); return; } reset( geom.type() ); addGeometry( geom, layer ); }
bool QgsSpatialQuery::hasValidGeometry( QgsFeature &feature ) { if ( !feature.isValid() ) return false; QgsGeometry geom = feature.geometry(); if ( geom.isNull() || geom.isEmpty() ) return false; return true; } // bool QgsSpatialQuery::hasValidGeometry(QgsFeature &feature)
QgsGeometry QgsMapToolDeleteRing::ringUnderPoint( const QgsPoint& p, QgsFeatureId& fid, int& partNum, int& ringNum ) { //There is no clean way to find if we are inside the ring of a feature, //so we iterate over all the features visible in the canvas //If several rings are found at this position, the smallest one is chosen, //in order to be able to delete a ring inside another ring QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( toLayerCoordinates( vlayer, mCanvas->extent() ) ) ); QgsFeature f; QgsGeometry g; QgsGeometry ringGeom; QgsMultiPolygon pol; QgsPolygon tempPol; QgsGeometry tempGeom; double area = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { g = f.geometry(); if ( g.isEmpty() ) continue; if ( g.wkbType() == QgsWkbTypes::Polygon || g.wkbType() == QgsWkbTypes::Polygon25D ) { pol = QgsMultiPolygon() << g.asPolygon(); } else { pol = g.asMultiPolygon(); } for ( int i = 0; i < pol.size() ; ++i ) {//for each part if ( pol[i].size() > 1 ) { for ( int j = 1; j < pol[i].size();++j ) { tempPol = QgsPolygon() << pol[i][j]; tempGeom = QgsGeometry::fromPolygon( tempPol ); if ( tempGeom.area() < area && tempGeom.contains( &p ) ) { fid = f.id(); partNum = i; ringNum = j; area = tempGeom.area(); ringGeom = tempGeom; } } } } } return ringGeom; }
bool QgsTransectSample::otherTransectWithinDistance( const QgsGeometry& geom, double minDistLayerUnit, double minDistance, QgsSpatialIndex& sIndex, const QMap< QgsFeatureId, QgsGeometry >& lineFeatureMap, QgsDistanceArea& da ) { if ( geom.isEmpty() ) { return false; } QgsGeometry buffer = geom.buffer( minDistLayerUnit, 8 ); if ( buffer.isEmpty() ) { return false; } QgsRectangle rect = buffer.boundingBox(); QList<QgsFeatureId> lineIdList = sIndex.intersects( rect ); QList<QgsFeatureId>::const_iterator lineIdIt = lineIdList.constBegin(); for ( ; lineIdIt != lineIdList.constEnd(); ++lineIdIt ) { const QMap< QgsFeatureId, QgsGeometry >::const_iterator idMapIt = lineFeatureMap.find( *lineIdIt ); if ( idMapIt != lineFeatureMap.constEnd() ) { double dist = 0; QgsPoint pt1, pt2; closestSegmentPoints( geom, idMapIt.value(), dist, pt1, pt2 ); dist = da.measureLine( pt1, pt2 ); //convert degrees to meters if necessary if ( dist < minDistance ) { return true; } } } return false; }
ErrorList topolTest::checkValid( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent ) { Q_UNUSED( tolerance ); Q_UNUSED( layer1 ); Q_UNUSED( layer2 ); Q_UNUSED( isExtent ); int i = 0; ErrorList errorList; QgsFeature f; QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( ++i ); if ( testCancelled() ) break; QgsGeometry g = it->feature.geometry(); if ( g.isEmpty() ) { QgsMessageLog::logMessage( tr( "Invalid geometry in validity test." ), tr( "Topology plugin" ) ); continue; } GEOSGeometry* gGeos = g.exportToGeos(); if ( !gGeos ) continue; if ( !GEOSisValid_r( QgsGeometry::getGEOSHandler(), gGeos ) ) { QgsRectangle r = g.boundingBox(); QList<FeatureLayer> fls; fls << *it << *it; TopolErrorValid* err = new TopolErrorValid( r, g, fls ); errorList << err; } GEOSGeom_destroy_r( QgsGeometry::getGEOSHandler(), gGeos ); } return errorList; }
QgsGeometry QgsGeometryAnalyzer::dissolveFeature( const QgsFeature& f, const QgsGeometry& dissolveInto ) { if ( !f.hasGeometry() ) { return dissolveInto; } QgsGeometry featureGeometry = f.geometry(); if ( dissolveInto.isEmpty() ) { return featureGeometry; } else { return dissolveInto.combine( featureGeometry ); } }
//! Makes sure that what came out from difference of two geometries is good to be used in the output static bool sanitizeDifferenceResult( QgsGeometry &geom ) { if ( geom.isNull() ) { // TODO: not sure if this ever happens - if it does, that means GEOS failed badly - would be good to have a test for such situation throw QgsProcessingException( QStringLiteral( "%1\n\n%2" ).arg( QObject::tr( "GEOS geoprocessing error: difference failed." ), geom.lastError() ) ); } // if geomB covers the whole source geometry, we get an empty geometry collection if ( geom.isEmpty() ) return false; // some data providers are picky about the geometries we pass to them: we can't add single-part geometries // when we promised multi-part geometries, so ensure we have the right type geom.convertToMultiType(); return true; }
//! Returns a simplified version the specified geometry (Removing duplicated points) when is applied the specified map2pixel context QgsGeometry QgsMapToPixelSimplifier::simplify( const QgsGeometry& geometry ) const { if ( geometry.isEmpty() ) { return QgsGeometry(); } if ( mSimplifyFlags == QgsMapToPixelSimplifier::NoFlags ) { return geometry; } // Check whether the geometry can be simplified using the map2pixel context const QgsWkbTypes::Type singleType = QgsWkbTypes::singleType( geometry.wkbType() ); const QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( singleType ); if ( flatType == QgsWkbTypes::Point ) { return geometry; } const bool isaLinearRing = flatType == QgsWkbTypes::Polygon; const int numPoints = geometry.geometry()->nCoordinates(); if ( numPoints <= ( isaLinearRing ? 6 : 3 ) ) { // No simplify simple geometries return geometry; } const QgsRectangle envelope = geometry.boundingBox(); if ( qMax( envelope.width(), envelope.height() ) / numPoints > mTolerance * 2.0 ) { //points are in average too far apart to lead to any significant simplification return geometry; } return simplifyGeometry( mSimplifyFlags, mSimplifyAlgorithm, geometry.wkbType(), *geometry.geometry(), envelope, mTolerance, false ); }
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( WktPrefixRegexp ) >= 0 ) mWktHasPrefix = true; geom = geomFromWkt( sWkt, mWktHasPrefix ); if ( !geom.isEmpty() ) { 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.setFeatureId( 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 { QgsPoint 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 && 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 = 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 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, 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() ) { if ( csvtTypes[i] == QLatin1String( "integer" ) ) { fieldType = QVariant::Int; typeName = QStringLiteral( "integer" ); } else if ( csvtTypes[i] == QLatin1String( "long" ) || csvtTypes[i] == QLatin1String( "longlong" ) || csvtTypes[i] == QLatin1String( "int8" ) ) { fieldType = QVariant::LongLong; //QVariant doesn't support long typeName = QStringLiteral( "longlong" ); } else if ( csvtTypes[i] == QLatin1String( "real" ) || csvtTypes[i] == QLatin1String( "double" ) ) { fieldType = QVariant::Double; typeName = QStringLiteral( "double" ); } } else if ( i < couldBeInt.size() ) { if ( couldBeInt[i] ) { fieldType = QVariant::Int; typeName = QStringLiteral( "integer" ); } else if ( couldBeLongLong[i] ) { fieldType = QVariant::LongLong; typeName = QStringLiteral( "longlong" ); } else if ( couldBeDouble[i] ) { fieldType = QVariant::Double; typeName = QStringLiteral( "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 != QgsWkbTypes::UnknownGeometry; mLayerValid = mValid; // If it is valid, then watch for changes to the file connect( mFile, SIGNAL( fileUpdated() ), this, SLOT( onFileUpdated() ) ); }
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; } QgsWkbTypes::Type outputType = QgsWkbTypes::Polygon; if ( dissolve ) { outputType = QgsWkbTypes::MultiPolygon; } QgsCoordinateReferenceSystem crs = layer->crs(); QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, crs ); QgsFeature currentFeature; QgsGeometry dissolveGeometry; //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.isEmpty() ) { QgsDebugMsg( "no dissolved geometry - should not happen" ); return false; } dissolveFeature.setGeometry( dissolveGeometry ); vWriter.addFeature( dissolveFeature ); } return true; }
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 ) { QgsWkbTypes::Type memoryProviderType = QgsWkbTypes::MultiLineString; if ( locationField2 == -1 ) { memoryProviderType = forceSingleGeometry ? QgsWkbTypes::Point : QgsWkbTypes::MultiPoint; } else { memoryProviderType = forceSingleGeometry ? QgsWkbTypes::LineString : QgsWkbTypes::MultiLineString; } 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; 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->geometry() ); } else { lrsGeom = locateBetweenMeasures( measure1, measure2, featureIdIt->geometry() ); } if ( !lrsGeom.isEmpty() ) { ++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; }
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( QStringLiteral( "UID" ), QVariant::String ) ); fields.append( QgsField( QStringLiteral( "AREA" ), QVariant::Double ) ); fields.append( QgsField( QStringLiteral( "PERIM" ), QVariant::Double ) ); QgsWkbTypes::Type outputType = QgsWkbTypes::Polygon; QgsCoordinateReferenceSystem crs = layer->crs(); QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, crs ); QgsFeature currentFeature; QgsGeometry dissolveGeometry; //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.isEmpty() ) { 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; if ( dissolveGeometry.isEmpty() ) { 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; }
int QgsTransectSample::createSample( QProgressDialog* pd ) { Q_UNUSED( pd ); if ( !mStrataLayer || !mStrataLayer->isValid() ) { return 1; } if ( !mBaselineLayer || !mBaselineLayer->isValid() ) { return 2; } //stratum id is not necessarily an integer QVariant::Type stratumIdType = QVariant::Int; if ( !mStrataIdAttribute.isEmpty() ) { stratumIdType = mStrataLayer->fields().field( mStrataIdAttribute ).type(); } //create vector file writers for output QgsFields outputPointFields; outputPointFields.append( QgsField( "id", stratumIdType ) ); outputPointFields.append( QgsField( "station_id", QVariant::Int ) ); outputPointFields.append( QgsField( "stratum_id", stratumIdType ) ); outputPointFields.append( QgsField( "station_code", QVariant::String ) ); outputPointFields.append( QgsField( "start_lat", QVariant::Double ) ); outputPointFields.append( QgsField( "start_long", QVariant::Double ) ); QgsVectorFileWriter outputPointWriter( mOutputPointLayer, "utf-8", outputPointFields, QgsWkbTypes::Point, mStrataLayer->crs() ); if ( outputPointWriter.hasError() != QgsVectorFileWriter::NoError ) { return 3; } outputPointFields.append( QgsField( "bearing", QVariant::Double ) ); //add bearing attribute for lines QgsVectorFileWriter outputLineWriter( mOutputLineLayer, "utf-8", outputPointFields, QgsWkbTypes::LineString, mStrataLayer->crs() ); if ( outputLineWriter.hasError() != QgsVectorFileWriter::NoError ) { return 4; } QgsFields usedBaselineFields; usedBaselineFields.append( QgsField( "stratum_id", stratumIdType ) ); usedBaselineFields.append( QgsField( "ok", QVariant::String ) ); QgsVectorFileWriter usedBaselineWriter( mUsedBaselineLayer, "utf-8", usedBaselineFields, QgsWkbTypes::LineString, mStrataLayer->crs() ); if ( usedBaselineWriter.hasError() != QgsVectorFileWriter::NoError ) { return 5; } //debug: write clipped buffer bounds with stratum id to same directory as out_point QFileInfo outputPointInfo( mOutputPointLayer ); QString bufferClipLineOutput = outputPointInfo.absolutePath() + "/out_buffer_clip_line.shp"; QgsFields bufferClipLineFields; bufferClipLineFields.append( QgsField( "id", stratumIdType ) ); QgsVectorFileWriter bufferClipLineWriter( bufferClipLineOutput, "utf-8", bufferClipLineFields, QgsWkbTypes::LineString, mStrataLayer->crs() ); //configure distanceArea depending on minDistance units and output CRS QgsDistanceArea distanceArea; distanceArea.setSourceCrs( mStrataLayer->crs().srsid() ); if ( mMinDistanceUnits == Meters ) { distanceArea.setEllipsoidalMode( true ); } else { distanceArea.setEllipsoidalMode( false ); } //possibility to transform output points to lat/long QgsCoordinateTransform toLatLongTransform( mStrataLayer->crs(), QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ) ); //init random number generator mt_srand( QTime::currentTime().msec() ); QgsFeatureRequest fr; fr.setSubsetOfAttributes( QStringList() << mStrataIdAttribute << mMinDistanceAttribute << mNPointsAttribute, mStrataLayer->fields() ); QgsFeatureIterator strataIt = mStrataLayer->getFeatures( fr ); QgsFeature fet; int nTotalTransects = 0; int nFeatures = 0; if ( pd ) { pd->setMaximum( mStrataLayer->featureCount() ); } while ( strataIt.nextFeature( fet ) ) { if ( pd ) { pd->setValue( nFeatures ); } if ( pd && pd->wasCanceled() ) { break; } if ( !fet.hasGeometry() ) { continue; } QgsGeometry strataGeom = fet.geometry(); //find baseline for strata QVariant strataId = fet.attribute( mStrataIdAttribute ); QgsGeometry baselineGeom = findBaselineGeometry( strataId.isValid() ? strataId : -1 ); if ( baselineGeom.isEmpty() ) { continue; } double minDistance = fet.attribute( mMinDistanceAttribute ).toDouble(); double minDistanceLayerUnits = minDistance; //if minDistance is in meters and the data in degrees, we need to apply a rough conversion for the buffer distance double bufferDist = bufferDistance( minDistance ); if ( mMinDistanceUnits == Meters && mStrataLayer->crs().mapUnits() == QgsUnitTypes::DistanceDegrees ) { minDistanceLayerUnits = minDistance / 111319.9; } QgsGeometry clippedBaseline = strataGeom.intersection( baselineGeom ); if ( !clippedBaseline || clippedBaseline.wkbType() == QgsWkbTypes::Unknown ) { continue; } QgsGeometry* bufferLineClipped = clipBufferLine( strataGeom, &clippedBaseline, bufferDist ); if ( !bufferLineClipped ) { continue; } //save clipped baseline to file QgsFeature blFeature( usedBaselineFields ); blFeature.setGeometry( clippedBaseline ); blFeature.setAttribute( "stratum_id", strataId ); blFeature.setAttribute( "ok", "f" ); usedBaselineWriter.addFeature( blFeature ); //start loop to create random points along the baseline int nTransects = fet.attribute( mNPointsAttribute ).toInt(); int nCreatedTransects = 0; int nIterations = 0; int nMaxIterations = nTransects * 50; QgsSpatialIndex sIndex; //to check minimum distance QMap< QgsFeatureId, QgsGeometry > lineFeatureMap; while ( nCreatedTransects < nTransects && nIterations < nMaxIterations ) { double randomPosition = (( double )mt_rand() / MD_RAND_MAX ) * clippedBaseline.length(); QgsGeometry samplePoint = clippedBaseline.interpolate( randomPosition ); ++nIterations; if ( samplePoint.isEmpty() ) { continue; } QgsPoint sampleQgsPoint = samplePoint.asPoint(); QgsPoint latLongSamplePoint = toLatLongTransform.transform( sampleQgsPoint ); QgsFeature samplePointFeature( outputPointFields ); samplePointFeature.setGeometry( samplePoint ); samplePointFeature.setAttribute( "id", nTotalTransects + 1 ); samplePointFeature.setAttribute( "station_id", nCreatedTransects + 1 ); samplePointFeature.setAttribute( "stratum_id", strataId ); samplePointFeature.setAttribute( "station_code", strataId.toString() + '_' + QString::number( nCreatedTransects + 1 ) ); samplePointFeature.setAttribute( "start_lat", latLongSamplePoint.y() ); samplePointFeature.setAttribute( "start_long", latLongSamplePoint.x() ); //find closest point on clipped buffer line QgsPoint minDistPoint; int afterVertex; if ( bufferLineClipped->closestSegmentWithContext( sampleQgsPoint, minDistPoint, afterVertex ) < 0 ) { continue; } //bearing between sample point and min dist point (transect direction) double bearing = distanceArea.bearing( sampleQgsPoint, minDistPoint ) / M_PI * 180.0; QgsPoint ptFarAway( sampleQgsPoint.x() + ( minDistPoint.x() - sampleQgsPoint.x() ) * 1000000, sampleQgsPoint.y() + ( minDistPoint.y() - sampleQgsPoint.y() ) * 1000000 ); QgsPolyline lineFarAway; lineFarAway << sampleQgsPoint << ptFarAway; QgsGeometry lineFarAwayGeom = QgsGeometry::fromPolyline( lineFarAway ); QgsGeometry lineClipStratum = lineFarAwayGeom.intersection( strataGeom ); if ( lineClipStratum.isEmpty() ) { continue; } //cancel if distance between sample point and line is too large (line does not start at point if ( lineClipStratum.distance( samplePoint ) > 0.000001 ) { continue; } //if lineClipStratum is a multiline, take the part line closest to sampleQgsPoint if ( lineClipStratum.wkbType() == QgsWkbTypes::MultiLineString || lineClipStratum.wkbType() == QgsWkbTypes::MultiLineString25D ) { QgsGeometry singleLine = closestMultilineElement( sampleQgsPoint, lineClipStratum ); if ( !singleLine.isEmpty() ) { lineClipStratum = singleLine; } } //cancel if length of lineClipStratum is too small double transectLength = distanceArea.measureLength( lineClipStratum ); if ( transectLength < mMinTransectLength ) { continue; } //search closest existing profile. Cancel if dist < minDist if ( otherTransectWithinDistance( lineClipStratum, minDistanceLayerUnits, minDistance, sIndex, lineFeatureMap, distanceArea ) ) { continue; } QgsFeatureId fid( nCreatedTransects ); QgsFeature sampleLineFeature( outputPointFields, fid ); sampleLineFeature.setGeometry( lineClipStratum ); sampleLineFeature.setAttribute( "id", nTotalTransects + 1 ); sampleLineFeature.setAttribute( "station_id", nCreatedTransects + 1 ); sampleLineFeature.setAttribute( "stratum_id", strataId ); sampleLineFeature.setAttribute( "station_code", strataId.toString() + '_' + QString::number( nCreatedTransects + 1 ) ); sampleLineFeature.setAttribute( "start_lat", latLongSamplePoint.y() ); sampleLineFeature.setAttribute( "start_long", latLongSamplePoint.x() ); sampleLineFeature.setAttribute( "bearing", bearing ); outputLineWriter.addFeature( sampleLineFeature ); //add point to file writer here. //It can only be written if the corresponding transect has been as well outputPointWriter.addFeature( samplePointFeature ); sIndex.insertFeature( sampleLineFeature ); lineFeatureMap.insert( fid, lineClipStratum ); ++nTotalTransects; ++nCreatedTransects; } QgsFeature bufferClipFeature; bufferClipFeature.setGeometry( *bufferLineClipped ); delete bufferLineClipped; bufferClipFeature.setAttribute( "id", strataId ); bufferClipLineWriter.addFeature( bufferClipFeature ); //delete bufferLineClipped; ++nFeatures; } if ( pd ) { pd->setValue( mStrataLayer->featureCount() ); } return 0; }
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 QgsMapToolReshape::reshape( QgsVectorLayer *vlayer ) { QgsPointXY firstPoint = points().at( 0 ); QgsRectangle bbox( firstPoint.x(), firstPoint.y(), firstPoint.x(), firstPoint.y() ); for ( int i = 1; i < size(); ++i ) { bbox.combineExtentWith( points().at( i ).x(), points().at( i ).y() ); } QgsLineString reshapeLineString( points() ); if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) ) reshapeLineString.addZValue( defaultZValue() ); //query all the features that intersect bounding box of capture line QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bbox ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; int reshapeReturn; bool reshapeDone = false; bool isBinding = isBindingLine( vlayer, bbox ); vlayer->beginEditCommand( tr( "Reshape" ) ); while ( fit.nextFeature( f ) ) { //query geometry //call geometry->reshape(mCaptureList) //register changed geometry in vector layer QgsGeometry geom = f.geometry(); if ( !geom.isNull() ) { // in case of a binding line, we just want to update the line from // the starting point and not both side if ( isBinding && !geom.asPolyline().contains( points().first() ) ) continue; reshapeReturn = geom.reshapeGeometry( reshapeLineString ); if ( reshapeReturn == 0 ) { //avoid intersections on polygon layers if ( vlayer->geometryType() == QgsWkbTypes::PolygonGeometry ) { //ignore all current layer features as they should be reshaped too QHash<QgsVectorLayer *, QSet<QgsFeatureId> > ignoreFeatures; ignoreFeatures.insert( vlayer, vlayer->allFeatureIds() ); if ( geom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers(), ignoreFeatures ) != 0 ) { emit messageEmitted( tr( "An error was reported during intersection removal" ), Qgis::Critical ); vlayer->destroyEditCommand(); stopCapturing(); return; } if ( geom.isEmpty() ) //intersection removal might have removed the whole geometry { emit messageEmitted( tr( "The feature cannot be reshaped because the resulting geometry is empty" ), Qgis::Critical ); vlayer->destroyEditCommand(); return; } } vlayer->changeGeometry( f.id(), geom ); reshapeDone = true; } } } if ( reshapeDone ) { vlayer->endEditCommand(); } else { vlayer->destroyEditCommand(); } }
QVariantMap QgsClipAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsFeatureSource > featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !featureSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); std::unique_ptr< QgsFeatureSource > maskSource( parameterAsSource( parameters, QStringLiteral( "OVERLAY" ), context ) ); if ( !maskSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "OVERLAY" ) ) ); QString dest; QgsWkbTypes::GeometryType sinkType = QgsWkbTypes::geometryType( featureSource->wkbType() ); std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, featureSource->fields(), QgsWkbTypes::multiType( featureSource->wkbType() ), featureSource->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); // first build up a list of clip geometries QVector< QgsGeometry > clipGeoms; QgsFeatureIterator it = maskSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QList< int >() ).setDestinationCrs( featureSource->sourceCrs(), context.transformContext() ) ); QgsFeature f; while ( it.nextFeature( f ) ) { if ( f.hasGeometry() ) clipGeoms << f.geometry(); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); if ( clipGeoms.isEmpty() ) return outputs; // are we clipping against a single feature? if so, we can show finer progress reports bool singleClipFeature = false; QgsGeometry combinedClipGeom; if ( clipGeoms.length() > 1 ) { combinedClipGeom = QgsGeometry::unaryUnion( clipGeoms ); if ( combinedClipGeom.isEmpty() ) { throw QgsProcessingException( QObject::tr( "Could not create the combined clip geometry: %1" ).arg( combinedClipGeom.lastError() ) ); } singleClipFeature = false; } else { combinedClipGeom = clipGeoms.at( 0 ); singleClipFeature = true; } // use prepared geometries for faster intersection tests std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( combinedClipGeom.constGet() ) ); engine->prepareGeometry(); QgsFeatureIds testedFeatureIds; int i = -1; Q_FOREACH ( const QgsGeometry &clipGeom, clipGeoms ) { i++; if ( feedback->isCanceled() ) { break; } QgsFeatureIterator inputIt = featureSource->getFeatures( QgsFeatureRequest().setFilterRect( clipGeom.boundingBox() ) ); QgsFeatureList inputFeatures; QgsFeature f; while ( inputIt.nextFeature( f ) ) inputFeatures << f; if ( inputFeatures.isEmpty() ) continue; double step = 0; if ( singleClipFeature ) step = 100.0 / inputFeatures.length(); int current = 0; Q_FOREACH ( const QgsFeature &inputFeature, inputFeatures ) { if ( feedback->isCanceled() ) { break; } if ( !inputFeature.hasGeometry() ) continue; if ( testedFeatureIds.contains( inputFeature.id() ) ) { // don't retest a feature we have already checked continue; } testedFeatureIds.insert( inputFeature.id() ); if ( !engine->intersects( inputFeature.geometry().constGet() ) ) continue; QgsGeometry newGeometry; if ( !engine->contains( inputFeature.geometry().constGet() ) ) { QgsGeometry currentGeometry = inputFeature.geometry(); newGeometry = combinedClipGeom.intersection( currentGeometry ); if ( newGeometry.wkbType() == QgsWkbTypes::Unknown || QgsWkbTypes::flatType( newGeometry.wkbType() ) == QgsWkbTypes::GeometryCollection ) { QgsGeometry intCom = inputFeature.geometry().combine( newGeometry ); QgsGeometry intSym = inputFeature.geometry().symDifference( newGeometry ); newGeometry = intCom.difference( intSym ); } } else { // clip geometry totally contains feature geometry, so no need to perform intersection newGeometry = inputFeature.geometry(); } if ( !QgsOverlayUtils::sanitizeIntersectionResult( newGeometry, sinkType ) ) continue; QgsFeature outputFeature; outputFeature.setGeometry( newGeometry ); outputFeature.setAttributes( inputFeature.attributes() ); sink->addFeature( outputFeature, QgsFeatureSink::FastInsert ); if ( singleClipFeature ) feedback->setProgress( current * step ); } if ( !singleClipFeature ) { // coarse progress report for multiple clip geometries feedback->setProgress( 100.0 * static_cast< double >( i ) / clipGeoms.length() ); } }
void QgsOSMDatabase::exportSpatiaLiteWays( bool closed, const QString& tableName, const QStringList& tagKeys, const QStringList& notNullTagKeys ) { Q_UNUSED( tagKeys ); QString sqlInsertLine = QString( "INSERT INTO %1 VALUES (?" ).arg( quotedIdentifier( tableName ) ); for ( int i = 0; i < tagKeys.count(); ++i ) sqlInsertLine += QString( ",?" ); sqlInsertLine += ", GeomFromWKB(?, 4326))"; sqlite3_stmt* stmtInsert; if ( sqlite3_prepare_v2( mDatabase, sqlInsertLine.toUtf8().constData(), -1, &stmtInsert, nullptr ) != SQLITE_OK ) { mError = "Prepare SELECT FROM ways failed."; return; } QgsOSMWayIterator ways = listWays(); QgsOSMWay w; while (( w = ways.next() ).isValid() ) { QgsOSMTags t = tags( true, w.id() ); QgsPolyline polyline = wayPoints( w.id() ); if ( polyline.count() < 2 ) continue; // invalid way bool isArea = ( polyline.first() == polyline.last() ); // closed way? // filter out closed way that are not areas through tags if ( isArea && ( t.contains( "highway" ) || t.contains( "barrier" ) ) ) { // make sure tags that indicate areas are taken into consideration when deciding on a closed way is or isn't an area // and allow for a closed way to be exported both as a polygon and a line in case both area and non-area tags are present if (( t.value( "area" ) != "yes" && !t.contains( "amenity" ) && !t.contains( "landuse" ) && !t.contains( "building" ) && !t.contains( "natural" ) && !t.contains( "leisure" ) && !t.contains( "aeroway" ) ) || !closed ) isArea = false; } if ( closed != isArea ) continue; // skip if it's not what we're looking for //check not null tags bool skipNull = false; for ( int i = 0; i < notNullTagKeys.count() && !skipNull; ++i ) if ( !t.contains( notNullTagKeys[i] ) ) skipNull = true; if ( skipNull ) continue; QgsGeometry geom = closed ? QgsGeometry::fromPolygon( QgsPolygon() << polyline ) : QgsGeometry::fromPolyline( polyline ); int col = 0; sqlite3_bind_int64( stmtInsert, ++col, w.id() ); // tags for ( int i = 0; i < tagKeys.count(); ++i ) { if ( t.contains( tagKeys[i] ) ) sqlite3_bind_text( stmtInsert, ++col, t.value( tagKeys[i] ).toUtf8().constData(), -1, SQLITE_TRANSIENT ); else sqlite3_bind_null( stmtInsert, ++col ); } if ( !geom.isEmpty() ) sqlite3_bind_blob( stmtInsert, ++col, geom.asWkb(), ( int ) geom.wkbSize(), SQLITE_STATIC ); else sqlite3_bind_null( stmtInsert, ++col ); int insertRes = sqlite3_step( stmtInsert ); if ( insertRes != SQLITE_DONE ) { mError = QString( "Error inserting way %1 [%2]" ).arg( w.id() ).arg( insertRes ); break; } sqlite3_reset( stmtInsert ); sqlite3_clear_bindings( stmtInsert ); } sqlite3_finalize( stmtInsert ); }
void QgsRubberBand::addGeometry( const QgsGeometry& geom, QgsVectorLayer* layer ) { if ( geom.isEmpty() ) { return; } //maprender object of canvas const QgsMapSettings& ms = mMapCanvas->mapSettings(); int idx = mPoints.size(); switch ( geom.wkbType() ) { case QgsWkbTypes::Point: case QgsWkbTypes::Point25D: { QgsPoint pt; if ( layer ) { pt = ms.layerToMapCoordinates( layer, geom.asPoint() ); } else { pt = geom.asPoint(); } addPoint( pt, false, idx ); removeLastPoint( idx, false ); } break; case QgsWkbTypes::MultiPoint: case QgsWkbTypes::MultiPoint25D: { QgsMultiPoint mpt = geom.asMultiPoint(); for ( int i = 0; i < mpt.size(); ++i, ++idx ) { QgsPoint pt = mpt[i]; if ( layer ) { addPoint( ms.layerToMapCoordinates( layer, pt ), false, idx ); removeLastPoint( idx, false ); } else { addPoint( pt, false, idx ); removeLastPoint( idx, false ); } } } break; case QgsWkbTypes::LineString: case QgsWkbTypes::LineString25D: { QgsPolyline line = geom.asPolyline(); for ( int i = 0; i < line.count(); i++ ) { if ( layer ) { addPoint( ms.layerToMapCoordinates( layer, line[i] ), false, idx ); } else { addPoint( line[i], false, idx ); } } } break; case QgsWkbTypes::MultiLineString: case QgsWkbTypes::MultiLineString25D: { QgsMultiPolyline mline = geom.asMultiPolyline(); for ( int i = 0; i < mline.size(); ++i, ++idx ) { QgsPolyline line = mline[i]; if ( line.isEmpty() ) { --idx; } for ( int j = 0; j < line.size(); ++j ) { if ( layer ) { addPoint( ms.layerToMapCoordinates( layer, line[j] ), false, idx ); } else { addPoint( line[j], false, idx ); } } } } break; case QgsWkbTypes::Polygon: case QgsWkbTypes::Polygon25D: { QgsPolygon poly = geom.asPolygon(); QgsPolyline line = poly[0]; for ( int i = 0; i < line.count(); i++ ) { if ( layer ) { addPoint( ms.layerToMapCoordinates( layer, line[i] ), false, idx ); } else { addPoint( line[i], false, idx ); } } } break; case QgsWkbTypes::MultiPolygon: case QgsWkbTypes::MultiPolygon25D: { QgsMultiPolygon multipoly = geom.asMultiPolygon(); for ( int i = 0; i < multipoly.size(); ++i, ++idx ) { QgsPolygon poly = multipoly[i]; QgsPolyline line = poly[0]; for ( int j = 0; j < line.count(); ++j ) { if ( layer ) { addPoint( ms.layerToMapCoordinates( layer, line[j] ), false, idx ); } else { addPoint( line[j], false, idx ); } } } } break; case QgsWkbTypes::Unknown: default: return; } setVisible( true ); updateRect(); update(); }
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; } QgsPoint 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.isEmpty() ) { 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->selectedFeaturesIds(); mRubberBand = createRubberBand( vlayer->geometryType() ); QgsFeature feat; QgsFeatureIterator it = vlayer->selectedFeaturesIterator(); 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 = atan2( YDistance, XDistance ) * ( 180 / PI ); createRotationWidget(); if ( e->modifiers() & Qt::ShiftModifier ) { if ( mRotationWidget ) { mRotationWidget->setMagnet( 45 ); } } mRotationActive = true; return; } applyRotation( mRotation ); }
QgsLabelFeature* QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature& feat, QgsRenderContext &context, QgsGeometry* obstacleGeometry ) { const QgsMapSettings& mapSettings = mEngine->mapSettings(); const QgsDiagramRenderer* dr = mSettings.getRenderer(); if ( dr ) { QList<QgsDiagramSettings> settingList = dr->diagramSettings(); if ( !settingList.isEmpty() && settingList.at( 0 ).scaleBasedVisibility ) { double minScale = settingList.at( 0 ).minScaleDenominator; if ( minScale > 0 && context.rendererScale() < minScale ) { return nullptr; } double maxScale = settingList.at( 0 ).maxScaleDenominator; if ( maxScale > 0 && context.rendererScale() > maxScale ) { return nullptr; } } } //convert geom to geos QgsGeometry geom = feat.geometry(); QgsGeometry extentGeom = QgsGeometry::fromRect( mapSettings.visibleExtent() ); if ( !qgsDoubleNear( mapSettings.rotation(), 0.0 ) ) { //PAL features are prerotated, so extent also needs to be unrotated extentGeom.rotate( -mapSettings.rotation(), mapSettings.visibleExtent().center() ); } const GEOSGeometry* geos_geom = nullptr; QScopedPointer<QgsGeometry> scopedPreparedGeom; if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, mSettings.coordinateTransform(), &extentGeom ) ) { scopedPreparedGeom.reset( new QgsGeometry( QgsPalLabeling::prepareGeometry( geom, context, mSettings.coordinateTransform(), &extentGeom ) ) ); QgsGeometry* preparedGeom = scopedPreparedGeom.data(); if ( preparedGeom->isEmpty() ) return nullptr; geos_geom = preparedGeom->asGeos(); } else { geos_geom = geom.asGeos(); } if ( !geos_geom ) return nullptr; // invalid geometry GEOSGeometry* geomCopy = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom ); const GEOSGeometry* geosObstacleGeom = nullptr; QScopedPointer<QgsGeometry> scopedObstacleGeom; if ( mSettings.isObstacle() && obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( *obstacleGeometry, context, mSettings.coordinateTransform(), &extentGeom ) ) { QgsGeometry preparedObstacleGeom = QgsPalLabeling::prepareGeometry( *obstacleGeometry, context, mSettings.coordinateTransform(), &extentGeom ); geosObstacleGeom = preparedObstacleGeom.asGeos(); } else if ( mSettings.isObstacle() && obstacleGeometry ) { geosObstacleGeom = obstacleGeometry->asGeos(); } GEOSGeometry* geosObstacleGeomClone = nullptr; if ( geosObstacleGeom ) { geosObstacleGeomClone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geosObstacleGeom ); } double diagramWidth = 0; double diagramHeight = 0; if ( dr ) { QSizeF diagSize = dr->sizeMapUnits( feat, context ); if ( diagSize.isValid() ) { diagramWidth = diagSize.width(); diagramHeight = diagSize.height(); } } // feature to the layer bool alwaysShow = mSettings.showAllDiagrams(); int ddColX = mSettings.xPosColumn; int ddColY = mSettings.yPosColumn; double ddPosX = 0.0; double ddPosY = 0.0; bool ddPos = ( ddColX >= 0 && ddColY >= 0 ); if ( ddPos && ! feat.attribute( ddColX ).isNull() && ! feat.attribute( ddColY ).isNull() ) { bool posXOk, posYOk; ddPosX = feat.attribute( ddColX ).toDouble( &posXOk ); ddPosY = feat.attribute( ddColY ).toDouble( &posYOk ); if ( !posXOk || !posYOk ) { ddPos = false; } else { QgsCoordinateTransform ct = mSettings.coordinateTransform(); if ( ct.isValid() && !ct.isShortCircuited() ) { double z = 0; ct.transformInPlace( ddPosX, ddPosY, z ); } //data defined diagram position is always centered ddPosX -= diagramWidth / 2.0; ddPosY -= diagramHeight / 2.0; } } else ddPos = false; int ddColShow = mSettings.showColumn; if ( ddColShow >= 0 && ! feat.attribute( ddColShow ).isNull() ) { bool showOk; bool ddShow = feat.attribute( ddColShow ).toDouble( &showOk ); if ( showOk && ! ddShow ) return nullptr; } QgsDiagramLabelFeature* lf = new QgsDiagramLabelFeature( feat.id(), geomCopy, QSizeF( diagramWidth, diagramHeight ) ); lf->setHasFixedPosition( ddPos ); lf->setFixedPosition( QgsPoint( ddPosX, ddPosY ) ); lf->setHasFixedAngle( true ); lf->setFixedAngle( 0 ); lf->setAlwaysShow( alwaysShow ); lf->setIsObstacle( mSettings.isObstacle() ); lf->setZIndex( mSettings.getZIndex() ); if ( geosObstacleGeomClone ) { lf->setObstacleGeometry( geosObstacleGeomClone ); } if ( dr ) { //append the diagram attributes to lbl lf->setAttributes( feat.attributes() ); } QgsPoint ptZero = mapSettings.mapToPixel().toMapCoordinates( 0, 0 ); QgsPoint ptOne = mapSettings.mapToPixel().toMapCoordinates( 1, 0 ); lf->setDistLabel( ptOne.distance( ptZero ) * mSettings.distance() ); return lf; }