Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsalgorithmlineintersection.cpp
3 : : ---------------------
4 : : begin : April 2017
5 : : copyright : (C) 2017 by Nyall Dawson
6 : : email : nyall dot dawson at gmail dot com
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include "qgsalgorithmlineintersection.h"
19 : : #include "qgsgeometryengine.h"
20 : :
21 : : ///@cond PRIVATE
22 : :
23 : 0 : QString QgsLineIntersectionAlgorithm::name() const
24 : : {
25 : 0 : return QStringLiteral( "lineintersections" );
26 : : }
27 : :
28 : 0 : QString QgsLineIntersectionAlgorithm::displayName() const
29 : : {
30 : 0 : return QObject::tr( "Line intersections" );
31 : : }
32 : :
33 : 0 : QStringList QgsLineIntersectionAlgorithm::tags() const
34 : : {
35 : 0 : return QObject::tr( "line,intersection" ).split( ',' );
36 : 0 : }
37 : :
38 : 0 : QString QgsLineIntersectionAlgorithm::group() const
39 : : {
40 : 0 : return QObject::tr( "Vector overlay" );
41 : : }
42 : :
43 : 0 : QString QgsLineIntersectionAlgorithm::groupId() const
44 : : {
45 : 0 : return QStringLiteral( "vectoroverlay" );
46 : : }
47 : :
48 : 0 : void QgsLineIntersectionAlgorithm::initAlgorithm( const QVariantMap & )
49 : : {
50 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ),
51 : 0 : QObject::tr( "Input layer" ), QList< int >() << QgsProcessing::TypeVectorLine ) );
52 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INTERSECT" ),
53 : 0 : QObject::tr( "Intersect layer" ), QList< int >() << QgsProcessing::TypeVectorLine ) );
54 : :
55 : 0 : addParameter( new QgsProcessingParameterField(
56 : 0 : QStringLiteral( "INPUT_FIELDS" ),
57 : 0 : QObject::tr( "Input fields to keep (leave empty to keep all fields)" ), QVariant(),
58 : 0 : QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any,
59 : : true, true ) );
60 : 0 : addParameter( new QgsProcessingParameterField(
61 : 0 : QStringLiteral( "INTERSECT_FIELDS" ),
62 : 0 : QObject::tr( "Intersect fields to keep (leave empty to keep all fields)" ), QVariant(),
63 : 0 : QStringLiteral( "INTERSECT" ), QgsProcessingParameterField::Any,
64 : : true, true ) );
65 : :
66 : 0 : std::unique_ptr< QgsProcessingParameterString > prefix = std::make_unique< QgsProcessingParameterString >( QStringLiteral( "INTERSECT_FIELDS_PREFIX" ), QObject::tr( "Intersect fields prefix" ), QString(), false, true );
67 : 0 : prefix->setFlags( prefix->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
68 : 0 : addParameter( prefix.release() );
69 : :
70 : 0 : addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Intersections" ), QgsProcessing::TypeVectorPoint ) );
71 : 0 : }
72 : :
73 : 0 : QString QgsLineIntersectionAlgorithm::shortHelpString() const
74 : : {
75 : 0 : return QObject::tr( "This algorithm creates point features where the lines in the Intersect layer intersect the lines in the Input layer." );
76 : : }
77 : :
78 : 0 : QgsLineIntersectionAlgorithm *QgsLineIntersectionAlgorithm::createInstance() const
79 : : {
80 : 0 : return new QgsLineIntersectionAlgorithm();
81 : : }
82 : :
83 : 0 : QVariantMap QgsLineIntersectionAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
84 : : {
85 : 0 : std::unique_ptr< QgsFeatureSource > sourceA( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
86 : 0 : if ( !sourceA )
87 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
88 : :
89 : 0 : std::unique_ptr< QgsFeatureSource > sourceB( parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context ) );
90 : 0 : if ( !sourceB )
91 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INTERSECT" ) ) );
92 : :
93 : 0 : const QStringList fieldsA = parameterAsFields( parameters, QStringLiteral( "INPUT_FIELDS" ), context );
94 : 0 : const QStringList fieldsB = parameterAsFields( parameters, QStringLiteral( "INTERSECT_FIELDS" ), context );
95 : :
96 : 0 : QgsAttributeList fieldIndicesA = QgsProcessingUtils::fieldNamesToIndices( fieldsA, sourceA->fields() );
97 : 0 : QgsAttributeList fieldIndicesB = QgsProcessingUtils::fieldNamesToIndices( fieldsB, sourceB->fields() );
98 : :
99 : 0 : QString intersectFieldsPrefix = parameterAsString( parameters, QStringLiteral( "INTERSECT_FIELDS_PREFIX" ), context );
100 : 0 : QgsFields outFields = QgsProcessingUtils::combineFields(
101 : 0 : QgsProcessingUtils::indicesToFields( fieldIndicesA, sourceA->fields() ),
102 : 0 : QgsProcessingUtils::indicesToFields( fieldIndicesB, sourceB->fields() ),
103 : : intersectFieldsPrefix );
104 : :
105 : 0 : QString dest;
106 : 0 : std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outFields, QgsWkbTypes::Point, sourceA->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
107 : 0 : if ( !sink )
108 : 0 : throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
109 : :
110 : 0 : QgsSpatialIndex spatialIndex( sourceB->getFeatures( QgsFeatureRequest().setNoAttributes().setDestinationCrs( sourceA->sourceCrs(), context.transformContext() ) ), feedback );
111 : 0 : QgsFeature outFeature;
112 : 0 : QgsFeatureIterator features = sourceA->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( fieldIndicesA ) );
113 : 0 : double step = sourceA->featureCount() > 0 ? 100.0 / sourceA->featureCount() : 1;
114 : 0 : int i = 0;
115 : 0 : QgsFeature inFeatureA;
116 : 0 : while ( features.nextFeature( inFeatureA ) )
117 : : {
118 : 0 : i++;
119 : 0 : if ( feedback->isCanceled() )
120 : : {
121 : 0 : break;
122 : : }
123 : :
124 : 0 : if ( !inFeatureA.hasGeometry() )
125 : 0 : continue;
126 : :
127 : 0 : QgsGeometry inGeom = inFeatureA.geometry();
128 : 0 : QgsFeatureIds lines = qgis::listToSet( spatialIndex.intersects( inGeom.boundingBox() ) );
129 : 0 : if ( !lines.empty() )
130 : : {
131 : : // use prepared geometries for faster intersection tests
132 : 0 : std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( inGeom.constGet() ) );
133 : 0 : engine->prepareGeometry();
134 : :
135 : 0 : QgsFeatureRequest request = QgsFeatureRequest().setFilterFids( lines );
136 : 0 : request.setDestinationCrs( sourceA->sourceCrs(), context.transformContext() );
137 : 0 : request.setSubsetOfAttributes( fieldIndicesB );
138 : :
139 : 0 : QgsFeature inFeatureB;
140 : 0 : QgsFeatureIterator featuresB = sourceB->getFeatures( request );
141 : 0 : while ( featuresB.nextFeature( inFeatureB ) )
142 : : {
143 : 0 : if ( feedback->isCanceled() )
144 : : {
145 : 0 : break;
146 : : }
147 : :
148 : 0 : QgsGeometry tmpGeom = inFeatureB.geometry();
149 : 0 : if ( engine->intersects( tmpGeom.constGet() ) )
150 : : {
151 : 0 : QgsMultiPointXY points;
152 : 0 : QgsGeometry intersectGeom = inGeom.intersection( tmpGeom );
153 : 0 : QgsAttributes outAttributes;
154 : 0 : for ( int a : std::as_const( fieldIndicesA ) )
155 : : {
156 : 0 : outAttributes.append( inFeatureA.attribute( a ) );
157 : : }
158 : 0 : for ( int b : std::as_const( fieldIndicesB ) )
159 : : {
160 : 0 : outAttributes.append( inFeatureB.attribute( b ) );
161 : : }
162 : 0 : if ( QgsWkbTypes::flatType( intersectGeom.wkbType() ) == QgsWkbTypes::GeometryCollection )
163 : : {
164 : 0 : const QVector<QgsGeometry> geomCollection = intersectGeom.asGeometryCollection();
165 : 0 : for ( const QgsGeometry &part : geomCollection )
166 : : {
167 : 0 : if ( part.type() == QgsWkbTypes::PointGeometry )
168 : : {
169 : 0 : if ( part.isMultipart() )
170 : : {
171 : 0 : points = part.asMultiPoint();
172 : 0 : }
173 : : else
174 : : {
175 : 0 : points.append( part.asPoint() );
176 : : }
177 : 0 : }
178 : : }
179 : 0 : }
180 : 0 : else if ( intersectGeom.type() == QgsWkbTypes::PointGeometry )
181 : : {
182 : 0 : if ( intersectGeom.isMultipart() )
183 : : {
184 : 0 : points = intersectGeom.asMultiPoint();
185 : 0 : }
186 : : else
187 : : {
188 : 0 : points.append( intersectGeom.asPoint() );
189 : : }
190 : 0 : }
191 : 0 : for ( const QgsPointXY &j : std::as_const( points ) )
192 : : {
193 : 0 : outFeature.setGeometry( QgsGeometry::fromPointXY( j ) );
194 : 0 : outFeature.setAttributes( outAttributes );
195 : 0 : sink->addFeature( outFeature, QgsFeatureSink::FastInsert );
196 : : }
197 : 0 : }
198 : 0 : }
199 : 0 : }
200 : :
201 : 0 : feedback->setProgress( i * step );
202 : :
203 : 0 : }
204 : :
205 : 0 : QVariantMap outputs;
206 : 0 : outputs.insert( QStringLiteral( "OUTPUT" ), dest );
207 : 0 : return outputs;
208 : 0 : }
209 : :
210 : : ///@endcond
211 : :
212 : :
|