22
22
#include " qgsprocessingutils.h"
23
23
#include " qgsvectorlayer.h"
24
24
#include " qgsgeometry.h"
25
+ #include " qgsgeometryengine.h"
25
26
#include " qgswkbtypes.h"
26
27
27
28
// /@cond PRIVATE
@@ -47,7 +48,7 @@ QString QgsNativeAlgorithms::id() const
47
48
48
49
QString QgsNativeAlgorithms::name () const
49
50
{
50
- return tr ( " QGIS" );
51
+ return tr ( " QGIS (native c++) " );
51
52
}
52
53
53
54
bool QgsNativeAlgorithms::supportsNonFileBasedOutput () const
@@ -60,6 +61,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
60
61
addAlgorithm ( new QgsCentroidAlgorithm () );
61
62
addAlgorithm ( new QgsBufferAlgorithm () );
62
63
addAlgorithm ( new QgsDissolveAlgorithm () );
64
+ addAlgorithm ( new QgsClipAlgorithm () );
63
65
}
64
66
65
67
@@ -360,4 +362,156 @@ QVariantMap QgsDissolveAlgorithm::processAlgorithm( const QVariantMap ¶meter
360
362
return outputs;
361
363
}
362
364
365
+ QgsClipAlgorithm::QgsClipAlgorithm ()
366
+ {
367
+ addParameter ( new QgsProcessingParameterFeatureSource ( QStringLiteral ( " INPUT" ), QObject::tr ( " Input layer" ) ) );
368
+ addParameter ( new QgsProcessingParameterFeatureSource ( QStringLiteral ( " OVERLAY" ), QObject::tr ( " Clip layer" ), QList< int >() << QgsProcessingParameterDefinition::TypeVectorPolygon ) );
369
+
370
+ addParameter ( new QgsProcessingParameterFeatureSink ( QStringLiteral ( " OUTPUT" ), QObject::tr ( " Clipped" ) ) );
371
+ addOutput ( new QgsProcessingOutputVectorLayer ( QStringLiteral ( " OUTPUT" ), QObject::tr ( " Clipped" ) ) );
372
+ }
373
+
374
+ QString QgsClipAlgorithm::shortHelpString () const
375
+ {
376
+ return QObject::tr ( " This algorithm clips a vector layer using the polygons of an additional polygons layer. Only the parts of the features "
377
+ " in the input layer that falls within the polygons of the clipping layer will be added to the resulting layer.\n\n "
378
+ " The attributes of the features are not modified, although properties such as area or length of the features will "
379
+ " be modified by the clipping operation. If such properties are stored as attributes, those attributes will have to "
380
+ " be manually updated." );
381
+ }
382
+
383
+ QVariantMap QgsClipAlgorithm::processAlgorithm ( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const
384
+ {
385
+ std::unique_ptr< QgsFeatureSource > featureSource ( parameterAsSource ( parameters, QStringLiteral ( " INPUT" ), context ) );
386
+ if ( !featureSource )
387
+ return QVariantMap ();
388
+
389
+ std::unique_ptr< QgsFeatureSource > maskSource ( parameterAsSource ( parameters, QStringLiteral ( " OVERLAY" ), context ) );
390
+ if ( !maskSource )
391
+ return QVariantMap ();
392
+
393
+ QString dest;
394
+ std::unique_ptr< QgsFeatureSink > sink ( parameterAsSink ( parameters, QStringLiteral ( " OUTPUT" ), context, featureSource->fields (), QgsWkbTypes::multiType ( featureSource->wkbType () ), featureSource->sourceCrs (), dest ) );
395
+
396
+ if ( !sink )
397
+ return QVariantMap ();
398
+
399
+ // first build up a list of clip geometries
400
+ QList< QgsGeometry > clipGeoms;
401
+ QgsFeatureIterator it = maskSource->getFeatures ( QgsFeatureRequest ().setSubsetOfAttributes ( QList< int >() ).setDestinationCrs ( featureSource->sourceCrs () ) );
402
+ QgsFeature f;
403
+ while ( it.nextFeature ( f ) )
404
+ {
405
+ if ( f.hasGeometry () )
406
+ clipGeoms << f.geometry ();
407
+ }
408
+
409
+ if ( clipGeoms.isEmpty () )
410
+ return QVariantMap ();
411
+
412
+ // are we clipping against a single feature? if so, we can show finer progress reports
413
+ bool singleClipFeature = false ;
414
+ QgsGeometry combinedClipGeom;
415
+ if ( clipGeoms.length () > 1 )
416
+ {
417
+ combinedClipGeom = QgsGeometry::unaryUnion ( clipGeoms );
418
+ singleClipFeature = false ;
419
+ }
420
+ else
421
+ {
422
+ combinedClipGeom = clipGeoms.at ( 0 );
423
+ singleClipFeature = true ;
424
+ }
425
+
426
+ // use prepared geometries for faster intersection tests
427
+ std::unique_ptr< QgsGeometryEngine > engine ( QgsGeometry::createGeometryEngine ( combinedClipGeom.geometry () ) );
428
+ engine->prepareGeometry ();
429
+
430
+ QgsFeatureIds testedFeatureIds;
431
+
432
+ int i = -1 ;
433
+ Q_FOREACH ( const QgsGeometry &clipGeom, clipGeoms )
434
+ {
435
+ i++;
436
+ if ( feedback->isCanceled () )
437
+ {
438
+ break ;
439
+ }
440
+
441
+ QgsFeatureIterator inputIt = featureSource->getFeatures ( QgsFeatureRequest ().setFilterRect ( clipGeom.boundingBox () ) );
442
+ QgsFeatureList inputFeatures;
443
+ QgsFeature f;
444
+ while ( inputIt.nextFeature ( f ) )
445
+ inputFeatures << f;
446
+
447
+ if ( inputFeatures.isEmpty () )
448
+ continue ;
449
+
450
+ double step = 0 ;
451
+ if ( singleClipFeature )
452
+ step = 100.0 / inputFeatures.length ();
453
+
454
+ int current = 0 ;
455
+ Q_FOREACH ( const QgsFeature &inputFeature, inputFeatures )
456
+ {
457
+ if ( feedback->isCanceled () )
458
+ {
459
+ break ;
460
+ }
461
+
462
+ if ( !inputFeature.hasGeometry () )
463
+ continue ;
464
+
465
+ if ( testedFeatureIds.contains ( inputFeature.id () ) )
466
+ {
467
+ // don't retest a feature we have already checked
468
+ continue ;
469
+ }
470
+ testedFeatureIds.insert ( inputFeature.id () );
471
+
472
+ if ( !engine->intersects ( *inputFeature.geometry ().geometry () ) )
473
+ continue ;
474
+
475
+ QgsGeometry newGeometry;
476
+ if ( !engine->contains ( *inputFeature.geometry ().geometry () ) )
477
+ {
478
+ QgsGeometry currentGeometry = inputFeature.geometry ();
479
+ newGeometry = combinedClipGeom.intersection ( currentGeometry );
480
+ if ( newGeometry.wkbType () == QgsWkbTypes::Unknown || QgsWkbTypes::flatType ( newGeometry.geometry ()->wkbType () ) == QgsWkbTypes::GeometryCollection )
481
+ {
482
+ QgsGeometry intCom = inputFeature.geometry ().combine ( newGeometry );
483
+ QgsGeometry intSym = inputFeature.geometry ().symDifference ( newGeometry );
484
+ newGeometry = intCom.difference ( intSym );
485
+ }
486
+ }
487
+ else
488
+ {
489
+ // clip geometry totally contains feature geometry, so no need to perform intersection
490
+ newGeometry = inputFeature.geometry ();
491
+ }
492
+
493
+ QgsFeature outputFeature;
494
+ outputFeature.setGeometry ( newGeometry );
495
+ outputFeature.setAttributes ( inputFeature.attributes () );
496
+ sink->addFeature ( outputFeature );
497
+
498
+
499
+ if ( singleClipFeature )
500
+ feedback->setProgress ( current * step );
501
+ }
502
+
503
+ if ( !singleClipFeature )
504
+ {
505
+ // coarse progress report for multiple clip geometries
506
+ feedback->setProgress ( 100.0 * static_cast < double >( i ) / clipGeoms.length () );
507
+ }
508
+ }
509
+
510
+ QVariantMap outputs;
511
+ outputs.insert ( QStringLiteral ( " OUTPUT" ), dest );
512
+ return outputs;
513
+ }
514
+
515
+
363
516
// /@endcond
517
+
0 commit comments