void TestQgsFields::byIndex() { QgsFields fields; QgsField field( "testfield" ); fields.append( field ); QgsField field2( "testfield2" ); fields.append( field2 ); QCOMPARE( fields[0], field ); QCOMPARE( fields[1], field2 ); const QgsFields& constFields = fields; QCOMPARE( constFields[0], field ); QCOMPARE( constFields[1], field2 ); QCOMPARE( constFields.at( 0 ), field ); QCOMPARE( constFields.at( 1 ), field2 ); QCOMPARE( constFields.field( 0 ), field ); QCOMPARE( constFields.field( 1 ), field2 ); }
void TestQgsFields::exists() { QgsFields fields; QgsField field( QStringLiteral( "testfield" ) ); fields.append( field ); QVERIFY( !fields.exists( -1 ) ); QVERIFY( !fields.exists( 1 ) ); QVERIFY( fields.exists( 0 ) ); }
void TestQgsFields::extend() { QgsFields destination; QgsField field( "testfield" ); destination.append( field ); QgsField field2( "testfield2" ); destination.append( field2 ); QgsFields source; QgsField field3( "testfield3" ); source.append( field3, QgsFields::OriginJoin, 5 ); QgsField field4( "testfield4" ); source.append( field4 ); QCOMPARE( destination.count(), 2 ); destination.extend( source ); QCOMPARE( destination.count(), 4 ); QCOMPARE( destination.at( 2 ), field3 ); QCOMPARE( destination.at( 3 ), field4 ); }
void TestQgsFields::clear() { QgsFields original; QgsField field( "testfield" ); original.append( field ); QCOMPARE( original.count(), 1 ); QgsFields copy( original ); copy.clear(); QCOMPARE( copy.count(), 0 ); QCOMPARE( original.count(), 1 ); }
void TestQgsFields::fieldOriginIndex() { QgsFields fields; QgsField field( QString( "testfield" ) ); fields.append( field , QgsFields::OriginProvider, 5 ); QCOMPARE( fields.fieldOriginIndex( 0 ), 5 ); QgsField field2( QString( "testfield2" ) ); fields.append( field2, QgsFields::OriginProvider, 10 ); QCOMPARE( fields.fieldOriginIndex( 1 ), 10 ); QgsField field3( QString( "testfield3" ) ); //field origin index not specified with OriginProvider, should be automatic fields.append( field3, QgsFields::OriginProvider ); QCOMPARE( fields.fieldOriginIndex( 2 ), 2 ); QgsField field4( QString( "testfield4" ) ); //field origin index not specified with other than OriginProvider, should remain -1 fields.append( field4, QgsFields::OriginEdit ); QCOMPARE( fields.fieldOriginIndex( 3 ), -1 ); }
void TestQgsFields::indexFromName() { QgsFields fields; QgsField field( QString( "testfield" ) ); fields.append( field ); QgsField field2( QString( "testfield2" ) ); fields.append( field2 ); QgsField field3( QString( "testfield3" ) ); fields.append( field3 ); QCOMPARE( fields.indexFromName( QString( "bad" ) ), -1 ); QCOMPARE( fields.fieldNameIndex( QString( "bad" ) ), -1 ); QCOMPARE( fields.indexFromName( QString( "testfield" ) ), 0 ); QCOMPARE( fields.fieldNameIndex( QString( "testfield" ) ), 0 ); QCOMPARE( fields.indexFromName( QString( "testfield3" ) ), 2 ); QCOMPARE( fields.fieldNameIndex( QString( "testfield3" ) ), 2 ); //indexFromName is case sensitive, fieldNameIndex isn't QCOMPARE( fields.indexFromName( QString( "teStFiEld2" ) ), -1 ); QCOMPARE( fields.fieldNameIndex( QString( "teStFiEld2" ) ), 1 ); }
void QgsVectorLayerEditBuffer::updateFields( QgsFields& fields ) { // delete attributes from the higher indices to lower indices for ( int i = mDeletedAttributeIds.count() - 1; i >= 0; --i ) { fields.remove( mDeletedAttributeIds[i] ); } // add new fields for ( int i = 0; i < mAddedAttributes.count(); ++i ) { fields.append( mAddedAttributes[i], QgsFields::OriginEdit, i ); } }
void TestQgsFields::remove() { QgsFields fields; //test for no crash fields.remove( 1 ); QgsField field( "testfield" ); fields.append( field ); QgsField field2( "testfield2" ); fields.append( field2 ); //test for no crash fields.remove( -1 ); fields.remove( 5 ); //remove valid field fields.remove( 0 ); QCOMPARE( fields.count(), 1 ); QCOMPARE( fields.at( 0 ).name(), QString( "testfield2" ) ); QCOMPARE( fields.indexFromName( "testfield2" ), 0 ); }
void TestQgsFields::indexFromName() { QgsFields fields; QgsField field( QStringLiteral( "testfield" ) ); field.setAlias( QStringLiteral( "testfieldAlias" ) ); fields.append( field ); QgsField field2( QStringLiteral( "testfield2" ) ); fields.append( field2 ); QgsField field3( QStringLiteral( "testfield3" ) ); field3.setAlias( QStringLiteral( "" ) ); fields.append( field3 ); QCOMPARE( fields.lookupField( QString( "" ) ), -1 ); QCOMPARE( fields.lookupField( QString() ), -1 ); QCOMPARE( fields.indexFromName( QString( "bad" ) ), -1 ); QCOMPARE( fields.lookupField( QString( "bad" ) ), -1 ); QCOMPARE( fields.indexFromName( QString( "testfield" ) ), 0 ); QCOMPARE( fields.lookupField( QString( "testfield" ) ), 0 ); QCOMPARE( fields.indexFromName( QString( "testfield3" ) ), 2 ); QCOMPARE( fields.lookupField( QString( "testfield3" ) ), 2 ); //indexFromName is case sensitive, fieldNameIndex isn't QCOMPARE( fields.indexFromName( QString( "teStFiEld2" ) ), -1 ); QCOMPARE( fields.lookupField( QString( "teStFiEld2" ) ), 1 ); //test that fieldNameIndex prefers exact case matches over case insensitive matches QgsField sameNameDifferentCase( QStringLiteral( "teStFielD" ) ); fields.append( sameNameDifferentCase ); QCOMPARE( fields.lookupField( QString( "teStFielD" ) ), 3 ); //test that the alias is only matched with fieldNameIndex QCOMPARE( fields.indexFromName( "testfieldAlias" ), -1 ); QCOMPARE( fields.lookupField( "testfieldAlias" ), 0 ); QCOMPARE( fields.lookupField( "testfieldalias" ), 0 ); }
void TestQgsFields::asVariant() { QgsField field1; field1.setName( "name" ); QgsField field2; field2.setName( "name" ); QgsFields original; original.append( field1 ); original.append( field2 ); //convert to and from a QVariant QVariant var = QVariant::fromValue( original ); QVERIFY( var.isValid() ); QgsFields fromVar = qvariant_cast<QgsFields>( var ); QCOMPARE( fromVar, original ); }
void TestQgsFields::copy() { QgsFields original; //add field QgsField field( "testfield" ); original.append( field ); QCOMPARE( original.count(), 1 ); QgsFields copy( original ); QCOMPARE( copy.count(), 1 ); QVERIFY( copy == original ); QgsField copyfield( "copyfield" ); copy.append( copyfield ); QCOMPARE( copy.count(), 2 ); QCOMPARE( original.count(), 1 ); QVERIFY( copy != original ); }
void QgsVectorLayerEditBuffer::updateFields( QgsFields& fields ) { // delete attributes from the higher indices to lower indices for ( int i = mDeletedAttributeIds.count() - 1; i >= 0; --i ) { fields.remove( mDeletedAttributeIds[i] ); } // add new fields for ( int i = 0; i < mAddedAttributes.count(); ++i ) { fields.append( mAddedAttributes[i], QgsFields::OriginEdit, i ); } // rename fields QgsFieldNameMap::const_iterator renameIt = mRenamedAttributes.constBegin(); for ( ; renameIt != mRenamedAttributes.constEnd(); ++renameIt ) { fields[ renameIt.key()].setName( renameIt.value() ); } }
void QgsOverlayAnalyzer::combineFieldLists( QgsFields& fieldListA, const QgsFields& fieldListB ) { QList<QString> names; Q_FOREACH ( const QgsField& field, fieldListA ) names.append( field.name() ); for ( int idx = 0; idx < fieldListB.count(); ++idx ) { QgsField field = fieldListB[idx]; int count = 0; while ( names.contains( field.name() ) ) { QString name = QString( "%1_%2" ).arg( field.name() ).arg( count ); field = QgsField( name, field.type() ); ++count; } fieldListA.append( field ); names.append( field.name() ); } }
void TestQgsFields::dataStream() { QgsField original1; original1.setName( "name" ); original1.setType( QVariant::Int ); original1.setLength( 5 ); original1.setPrecision( 2 ); original1.setTypeName( "typename1" ); original1.setComment( "comment1" ); QgsField original2; original2.setName( "next name" ); original2.setType( QVariant::Double ); original2.setLength( 15 ); original2.setPrecision( 3 ); original2.setTypeName( "double" ); original2.setComment( "comment for field 2" ); QgsFields originalFields; originalFields.append( original1 ); originalFields.append( original2 ); QByteArray ba; QDataStream ds( &ba, QIODevice::ReadWrite ); ds << originalFields; QgsFields resultFields; ds.device()->seek( 0 ); ds >> resultFields; QCOMPARE( resultFields, originalFields ); QCOMPARE( resultFields.field( 0 ).typeName(), originalFields.field( 0 ).typeName() ); //typename is NOT required for equality QCOMPARE( resultFields.field( 0 ).comment(), originalFields.field( 0 ).comment() ); //comment is NOT required for equality QCOMPARE( resultFields.field( 1 ).typeName(), originalFields.field( 1 ).typeName() ); QCOMPARE( resultFields.field( 1 ).comment(), originalFields.field( 1 ).comment() ); }
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->pendingFields().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, QGis::WKBPoint, &( 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, QGis::WKBLineString, &( 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, QGis::WKBLineString, &( 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, QGis::WKBLineString, &( 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->pendingFields() ); 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.constGeometry() ) { continue; } const QgsGeometry* strataGeom = fet.constGeometry(); //find baseline for strata QVariant strataId = fet.attribute( mStrataIdAttribute ); QgsGeometry* baselineGeom = findBaselineGeometry( strataId.isValid() ? strataId : -1 ); if ( !baselineGeom ) { 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() == QGis::DecimalDegrees ) { minDistanceLayerUnits = minDistance / 111319.9; } QgsGeometry* clippedBaseline = strataGeom->intersection( baselineGeom ); if ( !clippedBaseline || clippedBaseline->wkbType() == QGis::WKBUnknown ) { delete clippedBaseline; continue; } QgsGeometry* bufferLineClipped = clipBufferLine( strataGeom, clippedBaseline, bufferDist ); if ( !bufferLineClipped ) { delete clippedBaseline; continue; } //save clipped baseline to file QgsFeature blFeature; 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 ) { continue; } QgsPoint sampleQgsPoint = samplePoint->asPoint(); QgsPoint latLongSamplePoint = toLatLongTransform.transform( sampleQgsPoint ); QgsFeature samplePointFeature; 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; QgsPolyline sampleLinePolyline; 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 ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } //cancel if distance between sample point and line is too large (line does not start at point if ( lineClipStratum->distance( *samplePoint ) > 0.000001 ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } //if lineClipStratum is a multiline, take the part line closest to sampleQgsPoint if ( lineClipStratum->wkbType() == QGis::WKBMultiLineString || lineClipStratum->wkbType() == QGis::WKBMultiLineString25D ) { QgsGeometry* singleLine = closestMultilineElement( sampleQgsPoint, lineClipStratum ); if ( singleLine ) { delete lineClipStratum; lineClipStratum = singleLine; } } //cancel if length of lineClipStratum is too small double transectLength = distanceArea.measure( lineClipStratum ); if ( transectLength < mMinTransectLength ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } //search closest existing profile. Cancel if dist < minDist if ( otherTransectWithinDistance( lineClipStratum, minDistanceLayerUnits, minDistance, sIndex, lineFeatureMap, distanceArea ) ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } QgsFeatureId fid( nCreatedTransects ); QgsFeature sampleLineFeature( 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 ); Q_NOWARN_DEPRECATED_PUSH lineFeatureMap.insert( fid, sampleLineFeature.geometryAndOwnership() ); Q_NOWARN_DEPRECATED_POP delete lineFarAwayGeom; ++nTotalTransects; ++nCreatedTransects; } delete clippedBaseline; QgsFeature bufferClipFeature; bufferClipFeature.setGeometry( bufferLineClipped ); bufferClipFeature.setAttribute( "id", strataId ); bufferClipLineWriter.addFeature( bufferClipFeature ); //delete bufferLineClipped; //delete all line geometries in spatial index QMap< QgsFeatureId, QgsGeometry* >::iterator featureMapIt = lineFeatureMap.begin(); for ( ; featureMapIt != lineFeatureMap.end(); ++featureMapIt ) { delete( featureMapIt.value() ); } lineFeatureMap.clear(); delete baselineGeom; ++nFeatures; } if ( pd ) { pd->setValue( mStrataLayer->featureCount() ); } return 0; }
QVariantMap QgsJoinWithLinesAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { if ( parameters.value( QStringLiteral( "SPOKES" ) ) == parameters.value( QStringLiteral( "HUBS" ) ) ) throw QgsProcessingException( QObject::tr( "Same layer given for both hubs and spokes" ) ); std::unique_ptr< QgsFeatureSource > hubSource( parameterAsSource( parameters, QStringLiteral( "HUBS" ), context ) ); std::unique_ptr< QgsFeatureSource > spokeSource( parameterAsSource( parameters, QStringLiteral( "SPOKES" ), context ) ); if ( !hubSource || !spokeSource ) return QVariantMap(); QString fieldHubName = parameterAsString( parameters, QStringLiteral( "HUB_FIELD" ), context ); int fieldHubIndex = hubSource->fields().lookupField( fieldHubName ); const QStringList hubFieldsToCopy = parameterAsFields( parameters, QStringLiteral( "HUB_FIELDS" ), context ); QString fieldSpokeName = parameterAsString( parameters, QStringLiteral( "SPOKE_FIELD" ), context ); int fieldSpokeIndex = spokeSource->fields().lookupField( fieldSpokeName ); const QStringList spokeFieldsToCopy = parameterAsFields( parameters, QStringLiteral( "SPOKE_FIELDS" ), context ); if ( fieldHubIndex < 0 || fieldSpokeIndex < 0 ) throw QgsProcessingException( QObject::tr( "Invalid ID field" ) ); QgsFields hubOutFields; QgsAttributeList hubFieldIndices; if ( hubFieldsToCopy.empty() ) { hubOutFields = hubSource->fields(); for ( int i = 0; i < hubOutFields.count(); ++i ) { hubFieldIndices << i; } } else { for ( const QString &field : hubFieldsToCopy ) { int index = hubSource->fields().lookupField( field ); if ( index >= 0 ) { hubFieldIndices << index; hubOutFields.append( hubSource->fields().at( index ) ); } } } QgsAttributeList hubFields2Fetch = hubFieldIndices; hubFields2Fetch << fieldHubIndex; QgsFields spokeOutFields; QgsAttributeList spokeFieldIndices; if ( spokeFieldsToCopy.empty() ) { spokeOutFields = spokeSource->fields(); for ( int i = 0; i < spokeOutFields.count(); ++i ) { spokeFieldIndices << i; } } else { for ( const QString &field : spokeFieldsToCopy ) { int index = spokeSource->fields().lookupField( field ); if ( index >= 0 ) { spokeFieldIndices << index; spokeOutFields.append( spokeSource->fields().at( index ) ); } } } QgsAttributeList spokeFields2Fetch = spokeFieldIndices; spokeFields2Fetch << fieldSpokeIndex; QgsFields fields = QgsProcessingUtils::combineFields( hubOutFields, spokeOutFields ); QgsWkbTypes::Type outType = QgsWkbTypes::LineString; bool hasZ = false; if ( QgsWkbTypes::hasZ( hubSource->wkbType() ) || QgsWkbTypes::hasZ( spokeSource->wkbType() ) ) { outType = QgsWkbTypes::addZ( outType ); hasZ = true; } bool hasM = false; if ( QgsWkbTypes::hasM( hubSource->wkbType() ) || QgsWkbTypes::hasM( spokeSource->wkbType() ) ) { outType = QgsWkbTypes::addM( outType ); hasM = true; } QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, outType, hubSource->sourceCrs() ) ); if ( !sink ) return QVariantMap(); auto getPointFromFeature = [hasZ, hasM]( const QgsFeature & feature )->QgsPoint { QgsPoint p; if ( feature.geometry().type() == QgsWkbTypes::PointGeometry && !feature.geometry().isMultipart() ) p = *static_cast< const QgsPoint *>( feature.geometry().constGet() ); else p = *static_cast< const QgsPoint *>( feature.geometry().pointOnSurface().constGet() ); if ( hasZ && !p.is3D() ) p.addZValue( 0 ); if ( hasM && !p.isMeasure() ) p.addMValue( 0 ); return p; }; QgsFeatureIterator hubFeatures = hubSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( hubFields2Fetch ) ); double step = hubSource->featureCount() > 0 ? 100.0 / hubSource->featureCount() : 1; int i = 0; QgsFeature hubFeature; while ( hubFeatures.nextFeature( hubFeature ) ) { i++; if ( feedback->isCanceled() ) { break; } feedback->setProgress( i * step ); if ( !hubFeature.hasGeometry() ) continue; QgsPoint hubPoint = getPointFromFeature( hubFeature ); // only keep selected attributes QgsAttributes hubAttributes; for ( int j = 0; j < hubFeature.attributes().count(); ++j ) { if ( !hubFieldIndices.contains( j ) ) continue; hubAttributes << hubFeature.attribute( j ); } QgsFeatureRequest spokeRequest = QgsFeatureRequest().setDestinationCrs( hubSource->sourceCrs(), context.transformContext() ); spokeRequest.setSubsetOfAttributes( spokeFields2Fetch ); spokeRequest.setFilterExpression( QgsExpression::createFieldEqualityExpression( fieldSpokeName, hubFeature.attribute( fieldHubIndex ) ) ); QgsFeatureIterator spokeFeatures = spokeSource->getFeatures( spokeRequest ); QgsFeature spokeFeature; while ( spokeFeatures.nextFeature( spokeFeature ) ) { if ( feedback->isCanceled() ) { break; } if ( !spokeFeature.hasGeometry() ) continue; QgsPoint spokePoint = getPointFromFeature( spokeFeature ); QgsGeometry line( new QgsLineString( QVector< QgsPoint >() << hubPoint << spokePoint ) ); QgsFeature outFeature; QgsAttributes outAttributes = hubAttributes; // only keep selected attributes QgsAttributes spokeAttributes; for ( int j = 0; j < spokeFeature.attributes().count(); ++j ) { if ( !spokeFieldIndices.contains( j ) ) continue; spokeAttributes << spokeFeature.attribute( j ); } outAttributes.append( spokeAttributes ); outFeature.setAttributes( outAttributes ); outFeature.setGeometry( line ); sink->addFeature( outFeature, QgsFeatureSink::FastInsert ); } } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }
QgsRasterIdentifyResult QgsAmsProvider::identify( const QgsPointXY &point, QgsRaster::IdentifyFormat format, const QgsRectangle &extent, int width, int height, int dpi ) { // http://resources.arcgis.com/en/help/rest/apiref/identify.html QgsDataSourceUri dataSource( dataSourceUri() ); QUrl queryUrl( dataSource.param( QStringLiteral( "url" ) ) + "/identify" ); queryUrl.addQueryItem( QStringLiteral( "f" ), QStringLiteral( "json" ) ); queryUrl.addQueryItem( QStringLiteral( "geometryType" ), QStringLiteral( "esriGeometryPoint" ) ); queryUrl.addQueryItem( QStringLiteral( "geometry" ), QStringLiteral( "{x: %1, y: %2}" ).arg( point.x(), 0, 'f' ).arg( point.y(), 0, 'f' ) ); // queryUrl.addQueryItem( "sr", mCrs.postgisSrid() ); queryUrl.addQueryItem( QStringLiteral( "layers" ), QStringLiteral( "all:%1" ).arg( dataSource.param( QStringLiteral( "layer" ) ) ) ); queryUrl.addQueryItem( QStringLiteral( "imageDisplay" ), QStringLiteral( "%1,%2,%3" ).arg( width ).arg( height ).arg( dpi ) ); queryUrl.addQueryItem( QStringLiteral( "mapExtent" ), QStringLiteral( "%1,%2,%3,%4" ).arg( extent.xMinimum(), 0, 'f' ).arg( extent.yMinimum(), 0, 'f' ).arg( extent.xMaximum(), 0, 'f' ).arg( extent.yMaximum(), 0, 'f' ) ); queryUrl.addQueryItem( QStringLiteral( "tolerance" ), QStringLiteral( "10" ) ); const QString authcfg = dataSource.param( QStringLiteral( "authcfg" ) ); const QVariantList queryResults = QgsArcGisRestUtils::queryServiceJSON( queryUrl, authcfg, mErrorTitle, mError ).value( QStringLiteral( "results" ) ).toList(); QMap<int, QVariant> entries; if ( format == QgsRaster::IdentifyFormatText ) { for ( const QVariant &result : queryResults ) { const QVariantMap resultMap = result.toMap(); QVariantMap attributesMap = resultMap[QStringLiteral( "attributes" )].toMap(); QString valueStr; for ( auto it = attributesMap.constBegin(); it != attributesMap.constEnd(); ++it ) { valueStr += QStringLiteral( "%1 = %2\n" ).arg( it.key(), it.value().toString() ); } entries.insert( entries.size(), valueStr ); } } else if ( format == QgsRaster::IdentifyFormatFeature ) { for ( const QVariant &result : queryResults ) { const QVariantMap resultMap = result.toMap(); QgsFields fields; const QVariantMap attributesMap = resultMap[QStringLiteral( "attributes" )].toMap(); QgsAttributes featureAttributes; for ( auto it = attributesMap.constBegin(); it != attributesMap.constEnd(); ++it ) { fields.append( QgsField( it.key(), QVariant::String, QStringLiteral( "string" ) ) ); featureAttributes.append( it.value().toString() ); } QgsCoordinateReferenceSystem crs; std::unique_ptr< QgsAbstractGeometry > geometry = QgsArcGisRestUtils::parseEsriGeoJSON( resultMap[QStringLiteral( "geometry" )].toMap(), resultMap[QStringLiteral( "geometryType" )].toString(), false, false, &crs ); QgsFeature feature( fields ); feature.setGeometry( QgsGeometry( std::move( geometry ) ) ); feature.setAttributes( featureAttributes ); feature.setValid( true ); QgsFeatureStore store( fields, crs ); QMap<QString, QVariant> params; params[QStringLiteral( "sublayer" )] = resultMap[QStringLiteral( "layerName" )].toString(); params[QStringLiteral( "featureType" )] = attributesMap[resultMap[QStringLiteral( "displayFieldName" )].toString()].toString(); store.setParams( params ); store.addFeature( feature ); entries.insert( entries.size(), qVariantFromValue( QList<QgsFeatureStore>() << store ) ); } } return QgsRasterIdentifyResult( format, entries ); }
QgsVirtualLayerDefinition QgsVirtualLayerDefinition::fromUrl( const QUrl& url ) { QgsVirtualLayerDefinition def; def.setFilePath( url.path() ); // regexp for column name const QString columnNameRx( "[a-zA-Z_\x80-\xFF][a-zA-Z0-9_\x80-\xFF]*" ); QgsFields fields; int layerIdx = 0; QList<QPair<QString, QString> > items = url.queryItems(); for ( int i = 0; i < items.size(); i++ ) { QString key = items.at( i ).first; QString value = items.at( i ).second; if ( key == "layer_ref" ) { layerIdx++; // layer id, with optional layer_name int pos = value.indexOf( ':' ); QString layerId, vlayerName; if ( pos == -1 ) { layerId = value; vlayerName = QString( "vtab%1" ).arg( layerIdx ); } else { layerId = value.left( pos ); vlayerName = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() ); } // add the layer to the list def.addSource( vlayerName, layerId ); } else if ( key == "layer" ) { layerIdx++; // syntax: layer=provider:url_encoded_source_URI(:name(:encoding)?)? int pos = value.indexOf( ':' ); if ( pos != -1 ) { QString providerKey, source, vlayerName, encoding = "UTF-8"; providerKey = value.left( pos ); int pos2 = value.indexOf( ':', pos + 1 ); if ( pos2 != -1 ) { source = QUrl::fromPercentEncoding( value.mid( pos + 1, pos2 - pos - 1 ).toUtf8() ); int pos3 = value.indexOf( ':', pos2 + 1 ); if ( pos3 != -1 ) { vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1, pos3 - pos2 - 1 ).toUtf8() ); encoding = value.mid( pos3 + 1 ); } else { vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1 ).toUtf8() ); } } else { source = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() ); vlayerName = QString( "vtab%1" ).arg( layerIdx ); } def.addSource( vlayerName, source, providerKey, encoding ); } } else if ( key == "geometry" ) { // geometry field definition, optional // geometry_column(:wkb_type:srid)? QRegExp reGeom( "(" + columnNameRx + ")(?::([a-zA-Z0-9]+):(\\d+))?" ); int pos = reGeom.indexIn( value ); if ( pos >= 0 ) { def.setGeometryField( reGeom.cap( 1 ) ); if ( reGeom.captureCount() > 1 ) { // not used by the spatialite provider for now ... QgsWKBTypes::Type wkbType = QgsWKBTypes::parseType( reGeom.cap( 2 ) ); if ( wkbType == QgsWKBTypes::Unknown ) { wkbType = static_cast<QgsWKBTypes::Type>( reGeom.cap( 2 ).toLong() ); } def.setGeometryWkbType( wkbType ); def.setGeometrySrid( reGeom.cap( 3 ).toLong() ); } } } else if ( key == "nogeometry" ) { def.setGeometryWkbType( QgsWKBTypes::NoGeometry ); } else if ( key == "uid" ) { def.setUid( value ); } else if ( key == "query" ) { // url encoded query def.setQuery( value ); } else if ( key == "field" ) { // field_name:type (int, real, text) QRegExp reField( "(" + columnNameRx + "):(int|real|text)" ); int pos = reField.indexIn( value ); if ( pos >= 0 ) { QString fieldName( reField.cap( 1 ) ); QString fieldType( reField.cap( 2 ) ); if ( fieldType == "int" ) { fields.append( QgsField( fieldName, QVariant::Int, fieldType ) ); } else if ( fieldType == "real" ) { fields.append( QgsField( fieldName, QVariant::Double, fieldType ) ); } if ( fieldType == "text" ) { fields.append( QgsField( fieldName, QVariant::String, fieldType ) ); } } } } def.setFields( fields ); return def; }
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 ); QgsFields outFieldsA; QgsAttributeList fieldsAIndices; if ( fieldsA.empty() ) { outFieldsA = sourceA->fields(); for ( int i = 0; i < outFieldsA.count(); ++i ) { fieldsAIndices << i; } } else { for ( const QString &field : fieldsA ) { int index = sourceA->fields().lookupField( field ); if ( index >= 0 ) { fieldsAIndices << index; outFieldsA.append( sourceA->fields().at( index ) ); } } } QgsFields outFieldsB; QgsAttributeList fieldsBIndices; if ( fieldsB.empty() ) { outFieldsB = sourceB->fields(); for ( int i = 0; i < outFieldsB.count(); ++i ) { fieldsBIndices << i; } } else { for ( const QString &field : fieldsB ) { int index = sourceB->fields().lookupField( field ); if ( index >= 0 ) { fieldsBIndices << index; outFieldsB.append( sourceB->fields().at( index ) ); } } } QgsFields outFields = QgsProcessingUtils::combineFields( outFieldsA, outFieldsB ); 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( fieldsAIndices ) ); 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( fieldsBIndices ); 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( fieldsAIndices ) ) { outAttributes.append( inFeatureA.attribute( a ) ); } for ( int b : qgis::as_const( fieldsBIndices ) ) { 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; }
QVariantMap QgsShortestPathPointToPointAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { loadCommonParams( parameters, context, feedback ); QgsFields fields; fields.append( QgsField( QStringLiteral( "start" ), QVariant::String ) ); fields.append( QgsField( QStringLiteral( "end" ), QVariant::String ) ); fields.append( QgsField( QStringLiteral( "cost" ), QVariant::Double ) ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::LineString, mNetwork->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); QgsPointXY startPoint = parameterAsPoint( parameters, QStringLiteral( "START_POINT" ), context, mNetwork->sourceCrs() ); QgsPointXY endPoint = parameterAsPoint( parameters, QStringLiteral( "END_POINT" ), context, mNetwork->sourceCrs() ); feedback->pushInfo( QObject::tr( "Building graph…" ) ); QVector< QgsPointXY > points; points << startPoint << endPoint; QVector< QgsPointXY > snappedPoints; mDirector->makeGraph( mBuilder.get(), points, snappedPoints, feedback ); feedback->pushInfo( QObject::tr( "Calculating shortest path…" ) ); QgsGraph *graph = mBuilder->graph(); int idxStart = graph->findVertex( snappedPoints[0] ); int idxEnd = graph->findVertex( snappedPoints[1] ); QVector< int > tree; QVector< double > costs; QgsGraphAnalyzer::dijkstra( graph, idxStart, 0, &tree, &costs ); if ( tree.at( idxEnd ) == -1 ) { throw QgsProcessingException( QObject::tr( "There is no route from start point to end point." ) ); } QVector<QgsPointXY> route; route.push_front( graph->vertex( idxEnd ).point() ); double cost = costs.at( idxEnd ); while ( idxEnd != idxStart ) { idxEnd = graph->edge( tree.at( idxEnd ) ).fromVertex(); route.push_front( graph->vertex( idxEnd ).point() ); } feedback->pushInfo( QObject::tr( "Writing results…" ) ); QgsGeometry geom = QgsGeometry::fromPolylineXY( route ); QgsFeature feat; feat.setFields( fields ); QgsAttributes attributes; attributes << startPoint.toString() << endPoint.toString() << cost / mMultiplier; feat.setGeometry( geom ); feat.setAttributes( attributes ); sink->addFeature( feat, QgsFeatureSink::FastInsert ); QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); outputs.insert( QStringLiteral( "TRAVEL_COST" ), cost / mMultiplier ); return outputs; }
QVariantMap QgsShortestPathPointToLayerAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { loadCommonParams( parameters, context, feedback ); QgsPointXY startPoint = parameterAsPoint( parameters, QStringLiteral( "START_POINT" ), context, mNetwork->sourceCrs() ); std::unique_ptr< QgsFeatureSource > endPoints( parameterAsSource( parameters, QStringLiteral( "END_POINTS" ), context ) ); if ( !endPoints ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "END_POINTS" ) ) ); QgsFields fields = endPoints->fields(); fields.append( QgsField( QStringLiteral( "start" ), QVariant::String ) ); fields.append( QgsField( QStringLiteral( "end" ), QVariant::String ) ); fields.append( QgsField( QStringLiteral( "cost" ), QVariant::Double ) ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::LineString, mNetwork->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); QVector< QgsPointXY > points; points.push_front( startPoint ); QHash< int, QgsAttributes > sourceAttributes; loadPoints( endPoints.get(), points, sourceAttributes, context, feedback ); feedback->pushInfo( QObject::tr( "Building graph…" ) ); QVector< QgsPointXY > snappedPoints; mDirector->makeGraph( mBuilder.get(), points, snappedPoints, feedback ); feedback->pushInfo( QObject::tr( "Calculating shortest paths…" ) ); QgsGraph *graph = mBuilder->graph(); int idxStart = graph->findVertex( snappedPoints[0] ); int idxEnd; QVector< int > tree; QVector< double > costs; QgsGraphAnalyzer::dijkstra( graph, idxStart, 0, &tree, &costs ); QVector<QgsPointXY> route; double cost; QgsFeature feat; feat.setFields( fields ); QgsAttributes attributes; int step = points.size() > 0 ? 100.0 / points.size() : 1; for ( int i = 1; i < points.size(); i++ ) { if ( feedback->isCanceled() ) { break; } idxEnd = graph->findVertex( snappedPoints[i] ); if ( tree.at( idxEnd ) == -1 ) { feedback->reportError( QObject::tr( "There is no route from start point (%1) to end point (%2)." ) .arg( startPoint.toString(), points[i].toString() ) ); feat.clearGeometry(); attributes = sourceAttributes.value( i ); attributes.append( QVariant() ); attributes.append( points[i].toString() ); feat.setAttributes( attributes ); sink->addFeature( feat, QgsFeatureSink::FastInsert ); continue; } route.clear(); route.push_front( graph->vertex( idxEnd ).point() ); cost = costs.at( idxEnd ); while ( idxEnd != idxStart ) { idxEnd = graph->edge( tree.at( idxEnd ) ).fromVertex(); route.push_front( graph->vertex( idxEnd ).point() ); } QgsGeometry geom = QgsGeometry::fromPolylineXY( route ); QgsFeature feat; feat.setFields( fields ); attributes = sourceAttributes.value( i ); attributes.append( startPoint.toString() ); attributes.append( points[i].toString() ); attributes.append( cost / mMultiplier ); feat.setAttributes( attributes ); feat.setGeometry( geom ); sink->addFeature( feat, QgsFeatureSink::FastInsert ); feedback->setProgress( i * step ); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }
int QgsWFSProvider::readAttributesFromSchema( QDomDocument& schemaDoc, QString& geometryAttribute, QgsFields& fields, QGis::WkbType& geomType ) { //get the <schema> root element QDomNodeList schemaNodeList = schemaDoc.elementsByTagNameNS( QgsWFSConstants::XMLSCHEMA_NAMESPACE, "schema" ); if ( schemaNodeList.length() < 1 ) { return 1; } QDomElement schemaElement = schemaNodeList.at( 0 ).toElement(); mApplicationNamespace = schemaElement.attribute( "targetNamespace" ); QDomElement complexTypeElement; //the <complexType> element corresponding to the feature type //find out, on which lines the first <element> or the first <complexType> occur. If <element> occurs first (mapserver), read the type of the relevant <complexType> tag. If <complexType> occurs first (geoserver), search for information about the feature type directly under this first complexType element int firstElementTagPos = schemaElement.elementsByTagNameNS( QgsWFSConstants::XMLSCHEMA_NAMESPACE, "element" ).at( 0 ).toElement().columnNumber(); int firstComplexTypeTagPos = schemaElement.elementsByTagNameNS( QgsWFSConstants::XMLSCHEMA_NAMESPACE, "complexType" ).at( 0 ).toElement().columnNumber(); if ( firstComplexTypeTagPos < firstElementTagPos ) { //geoserver complexTypeElement = schemaElement.elementsByTagNameNS( QgsWFSConstants::XMLSCHEMA_NAMESPACE, "complexType" ).at( 0 ).toElement(); } else { //UMN mapserver QString complexTypeType; QDomNodeList typeElementNodeList = schemaElement.elementsByTagNameNS( QgsWFSConstants::XMLSCHEMA_NAMESPACE, "element" ); QDomElement typeElement = typeElementNodeList.at( 0 ).toElement(); complexTypeType = typeElement.attribute( "type" ); if ( complexTypeType.isEmpty() ) { return 3; } //remove the namespace on complexTypeType if ( complexTypeType.contains( ':' ) ) { complexTypeType = complexTypeType.section( ':', 1, 1 ); } //find <complexType name=complexTypeType QDomNodeList complexTypeNodeList = schemaElement.elementsByTagNameNS( QgsWFSConstants::XMLSCHEMA_NAMESPACE, "complexType" ); for ( int i = 0; i < complexTypeNodeList.size(); ++i ) { if ( complexTypeNodeList.at( i ).toElement().attribute( "name" ) == complexTypeType ) { complexTypeElement = complexTypeNodeList.at( i ).toElement(); break; } } } if ( complexTypeElement.isNull() ) { return 4; } //we have the relevant <complexType> element. Now find out the geometry and the thematic attributes QDomNodeList attributeNodeList = complexTypeElement.elementsByTagNameNS( QgsWFSConstants::XMLSCHEMA_NAMESPACE, "element" ); if ( attributeNodeList.size() < 1 ) { return 5; } bool foundGeometryAttribute = false; for ( int i = 0; i < attributeNodeList.size(); ++i ) { QDomElement attributeElement = attributeNodeList.at( i ).toElement(); //attribute name QString name = attributeElement.attribute( "name" ); //attribute type QString type = attributeElement.attribute( "type" ); //is it a geometry attribute? //MH 090428: sometimes the <element> tags for geometry attributes have only attribute ref="gml:polygonProperty" and no name QRegExp gmlPT( "gml:(.*)PropertyType" ); // the GeometryAssociationType has been seen in #11785 if ( type.indexOf( gmlPT ) == 0 || type == "gml:GeometryAssociationType" || name.isEmpty() ) { foundGeometryAttribute = true; geometryAttribute = name; geomType = geomTypeFromPropertyType( geometryAttribute, gmlPT.cap( 1 ) ); } else //todo: distinguish between numerical and non-numerical types { QVariant::Type attributeType = QVariant::String; //string is default type if ( type.contains( "double", Qt::CaseInsensitive ) || type.contains( "float", Qt::CaseInsensitive ) || type.contains( "decimal", Qt::CaseInsensitive ) ) { attributeType = QVariant::Double; } else if ( type.contains( "int", Qt::CaseInsensitive ) ) { attributeType = QVariant::Int; } else if ( type.contains( "long", Qt::CaseInsensitive ) ) { attributeType = QVariant::LongLong; } fields.append( QgsField( name, attributeType, type ) ); } } if ( !foundGeometryAttribute ) { geomType = QGis::WKBNoGeometry; } return 0; }
QVariantMap QgsJoinWithLinesAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { if ( parameters.value( QStringLiteral( "SPOKES" ) ) == parameters.value( QStringLiteral( "HUBS" ) ) ) throw QgsProcessingException( QObject::tr( "Same layer given for both hubs and spokes" ) ); std::unique_ptr< QgsProcessingFeatureSource > hubSource( parameterAsSource( parameters, QStringLiteral( "HUBS" ), context ) ); if ( !hubSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "HUBS" ) ) ); std::unique_ptr< QgsProcessingFeatureSource > spokeSource( parameterAsSource( parameters, QStringLiteral( "SPOKES" ), context ) ); if ( !hubSource || !spokeSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "SPOKES" ) ) ); QString fieldHubName = parameterAsString( parameters, QStringLiteral( "HUB_FIELD" ), context ); int fieldHubIndex = hubSource->fields().lookupField( fieldHubName ); const QStringList hubFieldsToCopy = parameterAsFields( parameters, QStringLiteral( "HUB_FIELDS" ), context ); QString fieldSpokeName = parameterAsString( parameters, QStringLiteral( "SPOKE_FIELD" ), context ); int fieldSpokeIndex = spokeSource->fields().lookupField( fieldSpokeName ); const QStringList spokeFieldsToCopy = parameterAsFields( parameters, QStringLiteral( "SPOKE_FIELDS" ), context ); if ( fieldHubIndex < 0 || fieldSpokeIndex < 0 ) throw QgsProcessingException( QObject::tr( "Invalid ID field" ) ); const bool geodesic = parameterAsBool( parameters, QStringLiteral( "GEODESIC" ), context ); const double geodesicDistance = parameterAsDouble( parameters, QStringLiteral( "GEODESIC_DISTANCE" ), context ) * 1000; bool dynamicGeodesicDistance = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "GEODESIC_DISTANCE" ) ); QgsExpressionContext expressionContext = createExpressionContext( parameters, context, hubSource.get() ); QgsProperty geodesicDistanceProperty; if ( dynamicGeodesicDistance ) { geodesicDistanceProperty = parameters.value( QStringLiteral( "GEODESIC_DISTANCE" ) ).value< QgsProperty >(); } const bool splitAntimeridian = parameterAsBool( parameters, QStringLiteral( "ANTIMERIDIAN_SPLIT" ), context ); QgsDistanceArea da; da.setSourceCrs( hubSource->sourceCrs(), context.transformContext() ); da.setEllipsoid( context.project()->ellipsoid() ); QgsFields hubOutFields; QgsAttributeList hubFieldIndices; if ( hubFieldsToCopy.empty() ) { hubOutFields = hubSource->fields(); hubFieldIndices.reserve( hubOutFields.count() ); for ( int i = 0; i < hubOutFields.count(); ++i ) { hubFieldIndices << i; } } else { hubFieldIndices.reserve( hubOutFields.count() ); for ( const QString &field : hubFieldsToCopy ) { int index = hubSource->fields().lookupField( field ); if ( index >= 0 ) { hubFieldIndices << index; hubOutFields.append( hubSource->fields().at( index ) ); } } } QgsAttributeList hubFields2Fetch = hubFieldIndices; hubFields2Fetch << fieldHubIndex; QgsFields spokeOutFields; QgsAttributeList spokeFieldIndices; if ( spokeFieldsToCopy.empty() ) { spokeOutFields = spokeSource->fields(); spokeFieldIndices.reserve( spokeOutFields.count() ); for ( int i = 0; i < spokeOutFields.count(); ++i ) { spokeFieldIndices << i; } } else { for ( const QString &field : spokeFieldsToCopy ) { int index = spokeSource->fields().lookupField( field ); if ( index >= 0 ) { spokeFieldIndices << index; spokeOutFields.append( spokeSource->fields().at( index ) ); } } } QgsAttributeList spokeFields2Fetch = spokeFieldIndices; spokeFields2Fetch << fieldSpokeIndex; QgsFields fields = QgsProcessingUtils::combineFields( hubOutFields, spokeOutFields ); QgsWkbTypes::Type outType = geodesic ? QgsWkbTypes::MultiLineString : QgsWkbTypes::LineString; bool hasZ = false; if ( QgsWkbTypes::hasZ( hubSource->wkbType() ) || QgsWkbTypes::hasZ( spokeSource->wkbType() ) ) { outType = QgsWkbTypes::addZ( outType ); hasZ = true; } bool hasM = false; if ( QgsWkbTypes::hasM( hubSource->wkbType() ) || QgsWkbTypes::hasM( spokeSource->wkbType() ) ) { outType = QgsWkbTypes::addM( outType ); hasM = true; } QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, outType, hubSource->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); auto getPointFromFeature = [hasZ, hasM]( const QgsFeature & feature )->QgsPoint { QgsPoint p; if ( feature.geometry().type() == QgsWkbTypes::PointGeometry && !feature.geometry().isMultipart() ) p = *static_cast< const QgsPoint *>( feature.geometry().constGet() ); else p = *static_cast< const QgsPoint *>( feature.geometry().pointOnSurface().constGet() ); if ( hasZ && !p.is3D() ) p.addZValue( 0 ); if ( hasM && !p.isMeasure() ) p.addMValue( 0 ); return p; }; QgsFeatureIterator hubFeatures = hubSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( hubFields2Fetch ), QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks ); double step = hubSource->featureCount() > 0 ? 100.0 / hubSource->featureCount() : 1; int i = 0; QgsFeature hubFeature; while ( hubFeatures.nextFeature( hubFeature ) ) { i++; if ( feedback->isCanceled() ) { break; } feedback->setProgress( i * step ); if ( !hubFeature.hasGeometry() ) continue; QgsPoint hubPoint = getPointFromFeature( hubFeature ); // only keep selected attributes QgsAttributes hubAttributes; for ( int j = 0; j < hubFeature.attributes().count(); ++j ) { if ( !hubFieldIndices.contains( j ) ) continue; hubAttributes << hubFeature.attribute( j ); } QgsFeatureRequest spokeRequest = QgsFeatureRequest().setDestinationCrs( hubSource->sourceCrs(), context.transformContext() ); spokeRequest.setSubsetOfAttributes( spokeFields2Fetch ); spokeRequest.setFilterExpression( QgsExpression::createFieldEqualityExpression( fieldSpokeName, hubFeature.attribute( fieldHubIndex ) ) ); QgsFeatureIterator spokeFeatures = spokeSource->getFeatures( spokeRequest, QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks ); QgsFeature spokeFeature; while ( spokeFeatures.nextFeature( spokeFeature ) ) { if ( feedback->isCanceled() ) { break; } if ( !spokeFeature.hasGeometry() ) continue; QgsPoint spokePoint = getPointFromFeature( spokeFeature ); QgsGeometry line; if ( !geodesic ) { line = QgsGeometry( new QgsLineString( QVector< QgsPoint >() << hubPoint << spokePoint ) ); if ( splitAntimeridian ) line = da.splitGeometryAtAntimeridian( line ); } else { double distance = geodesicDistance; if ( dynamicGeodesicDistance ) { expressionContext.setFeature( hubFeature ); distance = geodesicDistanceProperty.valueAsDouble( expressionContext, distance ); } std::unique_ptr< QgsMultiLineString > ml = qgis::make_unique< QgsMultiLineString >(); std::unique_ptr< QgsLineString > l = qgis::make_unique< QgsLineString >( QVector< QgsPoint >() << hubPoint ); QVector< QVector< QgsPointXY > > points = da.geodesicLine( QgsPointXY( hubPoint ), QgsPointXY( spokePoint ), distance, splitAntimeridian ); QVector< QgsPointXY > points1 = points.at( 0 ); points1.pop_front(); if ( points.count() == 1 ) points1.pop_back(); QgsLineString geodesicPoints( points1 ); l->append( &geodesicPoints ); if ( points.count() == 1 ) l->addVertex( spokePoint ); ml->addGeometry( l.release() ); if ( points.count() > 1 ) { QVector< QgsPointXY > points2 = points.at( 1 ); points2.pop_back(); l = qgis::make_unique< QgsLineString >( points2 ); if ( hasZ ) l->addZValue( std::numeric_limits<double>::quiet_NaN() ); if ( hasM ) l->addMValue( std::numeric_limits<double>::quiet_NaN() ); l->addVertex( spokePoint ); ml->addGeometry( l.release() ); } line = QgsGeometry( std::move( ml ) ); } QgsFeature outFeature; QgsAttributes outAttributes = hubAttributes; // only keep selected attributes QgsAttributes spokeAttributes; for ( int j = 0; j < spokeFeature.attributes().count(); ++j ) { if ( !spokeFieldIndices.contains( j ) ) continue; spokeAttributes << spokeFeature.attribute( j ); } outAttributes.append( spokeAttributes ); outFeature.setAttributes( outAttributes ); outFeature.setGeometry( line ); sink->addFeature( outFeature, QgsFeatureSink::FastInsert ); } } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }
QVariantMap QgsTransectAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { Side orientation = static_cast< QgsTransectAlgorithm::Side >( parameterAsInt( parameters, QStringLiteral( "SIDE" ), context ) ); double angle = fabs( parameterAsDouble( parameters, QStringLiteral( "ANGLE" ), context ) ); bool dynamicAngle = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "ANGLE" ) ); QgsProperty angleProperty; if ( dynamicAngle ) angleProperty = parameters.value( QStringLiteral( "ANGLE" ) ).value< QgsProperty >(); double length = parameterAsDouble( parameters, QStringLiteral( "LENGTH" ), context ); bool dynamicLength = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "LENGTH" ) ); QgsProperty lengthProperty; if ( dynamicLength ) lengthProperty = parameters.value( QStringLiteral( "LENGTH" ) ).value< QgsProperty >(); if ( orientation == QgsTransectAlgorithm::Both ) length /= 2.0; std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !source ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); QgsExpressionContext expressionContext = createExpressionContext( parameters, context, dynamic_cast< QgsProcessingFeatureSource * >( source.get() ) ); QgsFields fields = source->fields(); fields.append( QgsField( QStringLiteral( "TR_FID" ), QVariant::Int, QString(), 20 ) ); fields.append( QgsField( QStringLiteral( "TR_ID" ), QVariant::Int, QString(), 20 ) ); fields.append( QgsField( QStringLiteral( "TR_SEGMENT" ), QVariant::Int, QString(), 20 ) ); fields.append( QgsField( QStringLiteral( "TR_ANGLE" ), QVariant::Double, QString(), 5, 2 ) ); fields.append( QgsField( QStringLiteral( "TR_LENGTH" ), QVariant::Double, QString(), 20, 6 ) ); fields.append( QgsField( QStringLiteral( "TR_ORIENT" ), QVariant::Int, QString(), 1 ) ); QgsWkbTypes::Type outputWkb = QgsWkbTypes::LineString; if ( QgsWkbTypes::hasZ( source->wkbType() ) ) outputWkb = QgsWkbTypes::addZ( outputWkb ); if ( QgsWkbTypes::hasM( source->wkbType() ) ) outputWkb = QgsWkbTypes::addM( outputWkb ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, outputWkb, source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); QgsFeatureIterator features = source->getFeatures( ); int current = -1; int number = 0; double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1; QgsFeature feat; while ( features.nextFeature( feat ) ) { current++; if ( feedback->isCanceled() ) { break; } feedback->setProgress( current * step ); if ( !feat.hasGeometry() ) continue; QgsGeometry inputGeometry = feat.geometry(); if ( dynamicLength || dynamicAngle ) { expressionContext.setFeature( feat ); } double evaluatedLength = length; if ( dynamicLength ) evaluatedLength = lengthProperty.valueAsDouble( context.expressionContext(), length ); double evaluatedAngle = angle; if ( dynamicAngle ) evaluatedAngle = angleProperty.valueAsDouble( context.expressionContext(), angle ); inputGeometry.convertToMultiType(); const QgsMultiLineString *multiLine = static_cast< const QgsMultiLineString * >( inputGeometry.constGet() ); for ( int id = 0; id < multiLine->numGeometries(); ++id ) { const QgsLineString *line = static_cast< const QgsLineString * >( multiLine->geometryN( id ) ); QgsAbstractGeometry::vertex_iterator it = line->vertices_begin(); while ( it != line->vertices_end() ) { QgsVertexId vertexId = it.vertexId(); int i = vertexId.vertex; QgsFeature outFeat; QgsAttributes attrs = feat.attributes(); attrs << current << number << i + 1 << evaluatedAngle << ( ( orientation == QgsTransectAlgorithm::Both ) ? evaluatedLength * 2 : evaluatedLength ) << orientation; outFeat.setAttributes( attrs ); double angleAtVertex = line->vertexAngle( vertexId ); outFeat.setGeometry( calcTransect( *it, angleAtVertex, evaluatedLength, orientation, evaluatedAngle ) ); sink->addFeature( outFeat, QgsFeatureSink::FastInsert ); number++; it++; } } } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }
QVariantMap QgsDbscanClusteringAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !source ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); const std::size_t minSize = static_cast< std::size_t>( parameterAsInt( parameters, QStringLiteral( "MIN_SIZE" ), context ) ); const double eps = parameterAsDouble( parameters, QStringLiteral( "EPS" ), context ); const bool borderPointsAreNoise = parameterAsBoolean( parameters, QStringLiteral( "DBSCAN*" ), context ); QgsFields outputFields = source->fields(); const QString clusterFieldName = parameterAsString( parameters, QStringLiteral( "FIELD_NAME" ), context ); QgsFields newFields; newFields.append( QgsField( clusterFieldName, QVariant::Int ) ); outputFields = QgsProcessingUtils::combineFields( outputFields, newFields ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outputFields, source->wkbType(), source->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); // build spatial index feedback->pushInfo( QObject::tr( "Building spatial index" ) ); QgsSpatialIndexKDBush index( *source, feedback ); if ( feedback->isCanceled() ) return QVariantMap(); // dbscan! feedback->pushInfo( QObject::tr( "Analysing clusters" ) ); std::unordered_map< QgsFeatureId, int> idToCluster; idToCluster.reserve( index.size() ); QgsFeatureIterator features = source->getFeatures( QgsFeatureRequest().setNoAttributes() ); const long featureCount = source->featureCount(); int clusterCount = 0; dbscan( minSize, eps, borderPointsAreNoise, featureCount, features, index, idToCluster, clusterCount, feedback ); // write clusters const double writeStep = featureCount > 0 ? 10.0 / featureCount : 1; features = source->getFeatures(); int i = 0; QgsFeature feat; while ( features.nextFeature( feat ) ) { i++; if ( feedback->isCanceled() ) { break; } feedback->setProgress( 90 + i * writeStep ); QgsAttributes attr = feat.attributes(); auto cluster = idToCluster.find( feat.id() ); if ( cluster != idToCluster.end() ) { attr << cluster->second; } else { attr << QVariant(); } feat.setAttributes( attr ); sink->addFeature( feat, QgsFeatureSink::FastInsert ); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); outputs.insert( QStringLiteral( "NUM_CLUSTERS" ), clusterCount ); return outputs; }
bool QgsGeometryAnalyzer::extent( QgsVectorLayer* layer, const QString& shapefileName, bool onlySelectedFeatures, QProgressDialog * ) { if ( !layer ) { return false; } QgsVectorDataProvider* dp = layer->dataProvider(); if ( !dp ) { return false; } QgsWkbTypes::Type outputType = QgsWkbTypes::Polygon; QgsCoordinateReferenceSystem crs = layer->crs(); QgsFields fields; fields.append( QgsField( QStringLiteral( "MINX" ), QVariant::Double ) ); fields.append( QgsField( QStringLiteral( "MINY" ), QVariant::Double ) ); fields.append( QgsField( QStringLiteral( "MAXX" ), QVariant::Double ) ); fields.append( QgsField( QStringLiteral( "MAXY" ), QVariant::Double ) ); fields.append( QgsField( QStringLiteral( "CNTX" ), QVariant::Double ) ); fields.append( QgsField( QStringLiteral( "CNTY" ), QVariant::Double ) ); fields.append( QgsField( QStringLiteral( "AREA" ), QVariant::Double ) ); fields.append( QgsField( QStringLiteral( "PERIM" ), QVariant::Double ) ); fields.append( QgsField( QStringLiteral( "HEIGHT" ), QVariant::Double ) ); fields.append( QgsField( QStringLiteral( "WIDTH" ), QVariant::Double ) ); QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, crs ); QgsRectangle rect; if ( onlySelectedFeatures ) // take only selection { rect = layer->boundingBoxOfSelected(); } else { rect = layer->extent(); } double minx = rect.xMinimum(); double miny = rect.yMinimum(); double maxx = rect.xMaximum(); double maxy = rect.yMaximum(); double height = rect.height(); double width = rect.width(); double cntx = minx + ( width / 2.0 ); double cnty = miny + ( height / 2.0 ); double area = width * height; double perim = ( 2 * width ) + ( 2 * height ); QgsFeature feat; QgsAttributes attrs( 10 ); attrs[0] = QVariant( minx ); attrs[1] = QVariant( miny ); attrs[2] = QVariant( maxx ); attrs[3] = QVariant( maxy ); attrs[4] = QVariant( cntx ); attrs[5] = QVariant( cnty ); attrs[6] = QVariant( area ); attrs[7] = QVariant( perim ); attrs[8] = QVariant( height ); attrs[9] = QVariant( width ); feat.setAttributes( attrs ); feat.setGeometry( QgsGeometry::fromRect( rect ) ); vWriter.addFeature( feat ); return true; }
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; }
QVariantMap QgsZonalHistogramAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsFeatureSource > zones( parameterAsSource( parameters, QStringLiteral( "INPUT_VECTOR" ), context ) ); if ( !zones ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT_VECTOR" ) ) ); long count = zones->featureCount(); double step = count > 0 ? 100.0 / count : 1; long current = 0; QList< double > uniqueValues; QMap< QgsFeatureId, QHash< double, qgssize > > featuresUniqueValues; // First loop through the zones to build up a list of unique values across all zones to determine sink fields list QgsFeatureRequest request; request.setNoAttributes(); if ( zones->sourceCrs() != mCrs ) { request.setDestinationCrs( mCrs, context.transformContext() ); } QgsFeatureIterator it = zones->getFeatures( request ); QgsFeature f; while ( it.nextFeature( f ) ) { if ( feedback && feedback->isCanceled() ) { break; } feedback->setProgress( current * step ); if ( !f.hasGeometry() ) { current++; continue; } QgsGeometry featureGeometry = f.geometry(); QgsRectangle featureRect = featureGeometry.boundingBox().intersect( mRasterExtent ); if ( featureRect.isEmpty() ) { current++; continue; } int nCellsX, nCellsY; QgsRectangle rasterBlockExtent; QgsRasterAnalysisUtils::cellInfoForBBox( mRasterExtent, featureRect, mCellSizeX, mCellSizeY, nCellsX, nCellsY, mNbCellsXProvider, mNbCellsYProvider, rasterBlockExtent ); QHash< double, qgssize > fUniqueValues; QgsRasterAnalysisUtils::statisticsFromMiddlePointTest( mRasterInterface.get(), mRasterBand, featureGeometry, nCellsX, nCellsY, mCellSizeX, mCellSizeY, rasterBlockExtent, [ &fUniqueValues]( double value ) { fUniqueValues[value]++; }, false ); if ( fUniqueValues.count() < 1 ) { // The cell resolution is probably larger than the polygon area. We switch to slower precise pixel - polygon intersection in this case // TODO: eventually deal with weight if needed QgsRasterAnalysisUtils::statisticsFromPreciseIntersection( mRasterInterface.get(), mRasterBand, featureGeometry, nCellsX, nCellsY, mCellSizeX, mCellSizeY, rasterBlockExtent, [ &fUniqueValues]( double value, double ) { fUniqueValues[value]++; }, false ); } for ( auto it = fUniqueValues.constBegin(); it != fUniqueValues.constEnd(); ++it ) { if ( uniqueValues.indexOf( it.key() ) == -1 ) { uniqueValues << it.key(); } featuresUniqueValues[f.id()][it.key()] += it.value(); } current++; } std::sort( uniqueValues.begin(), uniqueValues.end() ); QString fieldPrefix = parameterAsString( parameters, QStringLiteral( "COLUMN_PREFIX" ), context ); QgsFields newFields; for ( auto it = uniqueValues.constBegin(); it != uniqueValues.constEnd(); ++it ) { newFields.append( QgsField( QStringLiteral( "%1%2" ).arg( fieldPrefix, mHasNoDataValue && *it == mNodataValue ? QStringLiteral( "NODATA" ) : QString::number( *it ) ), QVariant::LongLong, QString(), -1, 0 ) ); } QgsFields fields = QgsProcessingUtils::combineFields( zones->fields(), newFields ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, zones->wkbType(), zones->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); it = zones->getFeatures( QgsFeatureRequest() ); while ( it.nextFeature( f ) ) { QgsAttributes attributes = f.attributes(); QHash< double, qgssize > fUniqueValues = featuresUniqueValues.value( f.id() ); for ( auto it = uniqueValues.constBegin(); it != uniqueValues.constEnd(); ++it ) { attributes += fUniqueValues.value( *it, 0 ); } QgsFeature outputFeature; outputFeature.setGeometry( f.geometry() ); outputFeature.setAttributes( attributes ); sink->addFeature( outputFeature, QgsFeatureSink::FastInsert ); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }