mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-17 00:04:02 -04:00
[FEATURE][processing] one-to-many join support to the join attribute table alg. (#6499)
This commit is contained in:
parent
2238b42501
commit
6f47b25037
@ -87,8 +87,8 @@ class SpatialJoin(QgisAlgorithm):
|
|||||||
'crosses': 'crosses'}
|
'crosses': 'crosses'}
|
||||||
|
|
||||||
self.methods = [
|
self.methods = [
|
||||||
self.tr('Create separate feature for each located feature'),
|
self.tr('Create separate feature for each located feature (one-to-one)'),
|
||||||
self.tr('Take attributes of the first located feature only')
|
self.tr('Take attributes of the first located feature only (one-to-many)')
|
||||||
]
|
]
|
||||||
|
|
||||||
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
|
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
|
||||||
|
41
python/plugins/processing/tests/testdata/expected/join_attribute_table_all_match.gfs
vendored
Normal file
41
python/plugins/processing/tests/testdata/expected/join_attribute_table_all_match.gfs
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<GMLFeatureClassList>
|
||||||
|
<GMLFeatureClass>
|
||||||
|
<Name>join_attribute_table</Name>
|
||||||
|
<ElementPath>join_attribute_table</ElementPath>
|
||||||
|
<GeometryType>1</GeometryType>
|
||||||
|
<SRSName>EPSG:4326</SRSName>
|
||||||
|
<DatasetSpecificInfo>
|
||||||
|
<FeatureCount>10</FeatureCount>
|
||||||
|
<ExtentXMin>0.00000</ExtentXMin>
|
||||||
|
<ExtentXMax>8.00000</ExtentXMax>
|
||||||
|
<ExtentYMin>-5.00000</ExtentYMin>
|
||||||
|
<ExtentYMax>3.00000</ExtentYMax>
|
||||||
|
</DatasetSpecificInfo>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>id</Name>
|
||||||
|
<ElementPath>id</ElementPath>
|
||||||
|
<Type>Integer</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>id2</Name>
|
||||||
|
<ElementPath>id2</ElementPath>
|
||||||
|
<Type>Integer</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>id_2</Name>
|
||||||
|
<ElementPath>id_2</ElementPath>
|
||||||
|
<Type>Integer</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>NUM_A</Name>
|
||||||
|
<ElementPath>NUM_A</ElementPath>
|
||||||
|
<Type>Real</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>ST_A</Name>
|
||||||
|
<ElementPath>ST_A</ElementPath>
|
||||||
|
<Type>String</Type>
|
||||||
|
<Width>8</Width>
|
||||||
|
</PropertyDefn>
|
||||||
|
</GMLFeatureClass>
|
||||||
|
</GMLFeatureClassList>
|
111
python/plugins/processing/tests/testdata/expected/join_attribute_table_all_match.gml
vendored
Normal file
111
python/plugins/processing/tests/testdata/expected/join_attribute_table_all_match.gml
vendored
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<ogr:FeatureCollection
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation=""
|
||||||
|
xmlns:ogr="http://ogr.maptools.org/"
|
||||||
|
xmlns:gml="http://www.opengis.net/gml">
|
||||||
|
<gml:boundedBy>
|
||||||
|
<gml:Box>
|
||||||
|
<gml:coord><gml:X>0</gml:X><gml:Y>-5</gml:Y></gml:coord>
|
||||||
|
<gml:coord><gml:X>8</gml:X><gml:Y>3</gml:Y></gml:coord>
|
||||||
|
</gml:Box>
|
||||||
|
</gml:boundedBy>
|
||||||
|
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:join_attribute_table fid="points.0">
|
||||||
|
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>1,1</gml:coordinates></gml:Point></ogr:geometryProperty>
|
||||||
|
<ogr:id>1</ogr:id>
|
||||||
|
<ogr:id2>2</ogr:id2>
|
||||||
|
<ogr:id_2>1</ogr:id_2>
|
||||||
|
<ogr:NUM_A>1.100000</ogr:NUM_A>
|
||||||
|
<ogr:ST_A>string a</ogr:ST_A>
|
||||||
|
</ogr:join_attribute_table>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:join_attribute_table fid="points.1">
|
||||||
|
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>3,3</gml:coordinates></gml:Point></ogr:geometryProperty>
|
||||||
|
<ogr:id>2</ogr:id>
|
||||||
|
<ogr:id2>1</ogr:id2>
|
||||||
|
<ogr:id_2>2</ogr:id_2>
|
||||||
|
<ogr:NUM_A>2.400000</ogr:NUM_A>
|
||||||
|
<ogr:ST_A>string c</ogr:ST_A>
|
||||||
|
</ogr:join_attribute_table>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:join_attribute_table fid="points.1">
|
||||||
|
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>3,3</gml:coordinates></gml:Point></ogr:geometryProperty>
|
||||||
|
<ogr:id>2</ogr:id>
|
||||||
|
<ogr:id2>1</ogr:id2>
|
||||||
|
<ogr:id_2>2</ogr:id_2>
|
||||||
|
<ogr:NUM_A>2.200000</ogr:NUM_A>
|
||||||
|
<ogr:ST_A>string a</ogr:ST_A>
|
||||||
|
</ogr:join_attribute_table>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:join_attribute_table fid="points.2">
|
||||||
|
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>2,2</gml:coordinates></gml:Point></ogr:geometryProperty>
|
||||||
|
<ogr:id>3</ogr:id>
|
||||||
|
<ogr:id2>0</ogr:id2>
|
||||||
|
<ogr:id_2>3</ogr:id_2>
|
||||||
|
<ogr:NUM_A>3.300000</ogr:NUM_A>
|
||||||
|
<ogr:ST_A>string a</ogr:ST_A>
|
||||||
|
</ogr:join_attribute_table>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:join_attribute_table fid="points.3">
|
||||||
|
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>5,2</gml:coordinates></gml:Point></ogr:geometryProperty>
|
||||||
|
<ogr:id>4</ogr:id>
|
||||||
|
<ogr:id2>2</ogr:id2>
|
||||||
|
<ogr:id_2>4</ogr:id_2>
|
||||||
|
<ogr:NUM_A>4.400000</ogr:NUM_A>
|
||||||
|
<ogr:ST_A>string b</ogr:ST_A>
|
||||||
|
</ogr:join_attribute_table>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:join_attribute_table fid="points.4">
|
||||||
|
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>4,1</gml:coordinates></gml:Point></ogr:geometryProperty>
|
||||||
|
<ogr:id>5</ogr:id>
|
||||||
|
<ogr:id2>1</ogr:id2>
|
||||||
|
<ogr:id_2>5</ogr:id_2>
|
||||||
|
<ogr:NUM_A>5.500000</ogr:NUM_A>
|
||||||
|
<ogr:ST_A>string b</ogr:ST_A>
|
||||||
|
</ogr:join_attribute_table>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:join_attribute_table fid="points.5">
|
||||||
|
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>0,-5</gml:coordinates></gml:Point></ogr:geometryProperty>
|
||||||
|
<ogr:id>6</ogr:id>
|
||||||
|
<ogr:id2>0</ogr:id2>
|
||||||
|
<ogr:id_2>6</ogr:id_2>
|
||||||
|
<ogr:NUM_A>6.600000</ogr:NUM_A>
|
||||||
|
<ogr:ST_A>string b</ogr:ST_A>
|
||||||
|
</ogr:join_attribute_table>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:join_attribute_table fid="points.6">
|
||||||
|
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>8,-1</gml:coordinates></gml:Point></ogr:geometryProperty>
|
||||||
|
<ogr:id>7</ogr:id>
|
||||||
|
<ogr:id2>0</ogr:id2>
|
||||||
|
<ogr:id_2>7</ogr:id_2>
|
||||||
|
<ogr:NUM_A>7.700000</ogr:NUM_A>
|
||||||
|
<ogr:ST_A>string b</ogr:ST_A>
|
||||||
|
</ogr:join_attribute_table>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:join_attribute_table fid="points.7">
|
||||||
|
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>7,-1</gml:coordinates></gml:Point></ogr:geometryProperty>
|
||||||
|
<ogr:id>8</ogr:id>
|
||||||
|
<ogr:id2>0</ogr:id2>
|
||||||
|
<ogr:id_2>8</ogr:id_2>
|
||||||
|
<ogr:NUM_A>8.800000</ogr:NUM_A>
|
||||||
|
<ogr:ST_A>string b</ogr:ST_A>
|
||||||
|
</ogr:join_attribute_table>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:join_attribute_table fid="points.8">
|
||||||
|
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>0,-1</gml:coordinates></gml:Point></ogr:geometryProperty>
|
||||||
|
<ogr:id>9</ogr:id>
|
||||||
|
<ogr:id2>0</ogr:id2>
|
||||||
|
</ogr:join_attribute_table>
|
||||||
|
</gml:featureMember>
|
||||||
|
</ogr:FeatureCollection>
|
@ -2772,8 +2772,9 @@ tests:
|
|||||||
type: vector
|
type: vector
|
||||||
|
|
||||||
- algorithm: native:joinattributestable
|
- algorithm: native:joinattributestable
|
||||||
name: join the attribute table by common field
|
name: join the attribute table by common field (one-to-one)
|
||||||
params:
|
params:
|
||||||
|
METHOD: 0
|
||||||
INPUT:
|
INPUT:
|
||||||
name: points.gml
|
name: points.gml
|
||||||
type: vector
|
type: vector
|
||||||
@ -2787,9 +2788,27 @@ tests:
|
|||||||
name: expected/join_attribute_table.gml
|
name: expected/join_attribute_table.gml
|
||||||
type: vector
|
type: vector
|
||||||
|
|
||||||
|
- algorithm: native:joinattributestable
|
||||||
|
name: join the attribute table by common field (one-to-many)
|
||||||
|
params:
|
||||||
|
METHOD: 1
|
||||||
|
INPUT:
|
||||||
|
name: points.gml
|
||||||
|
type: vector
|
||||||
|
INPUT_2:
|
||||||
|
name: table.dbf
|
||||||
|
type: table
|
||||||
|
FIELD: id
|
||||||
|
FIELD_2: ID
|
||||||
|
results:
|
||||||
|
OUTPUT:
|
||||||
|
name: expected/join_attribute_table_all_match.gml
|
||||||
|
type: vector
|
||||||
|
|
||||||
- algorithm: native:joinattributestable
|
- algorithm: native:joinattributestable
|
||||||
name: Join attributes table with subset of fields
|
name: Join attributes table with subset of fields
|
||||||
params:
|
params:
|
||||||
|
METHOD: 0
|
||||||
FIELD: id
|
FIELD: id
|
||||||
FIELDS_TO_COPY:
|
FIELDS_TO_COPY:
|
||||||
- NUM_A
|
- NUM_A
|
||||||
|
BIN
python/plugins/processing/tests/testdata/table.dbf
vendored
BIN
python/plugins/processing/tests/testdata/table.dbf
vendored
Binary file not shown.
@ -46,6 +46,10 @@ QString QgsJoinByAttributeAlgorithm::groupId() const
|
|||||||
|
|
||||||
void QgsJoinByAttributeAlgorithm::initAlgorithm( const QVariantMap & )
|
void QgsJoinByAttributeAlgorithm::initAlgorithm( const QVariantMap & )
|
||||||
{
|
{
|
||||||
|
QStringList methods;
|
||||||
|
methods << QObject::tr( "Take attributes of the first matching feature only (one-to-one)" )
|
||||||
|
<< QObject::tr( "Create separate feature for each matching feature (one-to-many)" );
|
||||||
|
|
||||||
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ),
|
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ),
|
||||||
QObject::tr( "Input layer" ), QList< int>() << QgsProcessing::TypeVector ) );
|
QObject::tr( "Input layer" ), QList< int>() << QgsProcessing::TypeVector ) );
|
||||||
addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ),
|
addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ),
|
||||||
@ -61,6 +65,11 @@ void QgsJoinByAttributeAlgorithm::initAlgorithm( const QVariantMap & )
|
|||||||
QVariant(), QStringLiteral( "INPUT_2" ), QgsProcessingParameterField::Any,
|
QVariant(), QStringLiteral( "INPUT_2" ), QgsProcessingParameterField::Any,
|
||||||
true, true ) );
|
true, true ) );
|
||||||
|
|
||||||
|
addParameter( new QgsProcessingParameterEnum(
|
||||||
|
QStringLiteral( "METHOD" ),
|
||||||
|
QObject::tr( "Join type" ),
|
||||||
|
methods, false, 0 ) );
|
||||||
|
|
||||||
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Joined layer" ) ) );
|
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Joined layer" ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +78,8 @@ QString QgsJoinByAttributeAlgorithm::shortHelpString() const
|
|||||||
return QObject::tr( "This algorithm takes an input vector layer and creates a new vector layer that is an extended version of the "
|
return QObject::tr( "This algorithm takes an input vector layer and creates a new vector layer that is an extended version of the "
|
||||||
"input one, with additional attributes in its attribute table.\n\n"
|
"input one, with additional attributes in its attribute table.\n\n"
|
||||||
"The additional attributes and their values are taken from a second vector layer. An attribute is selected "
|
"The additional attributes and their values are taken from a second vector layer. An attribute is selected "
|
||||||
"in each of them to define the join criteria." );
|
"in each of them to define the join criteria.\n\n"
|
||||||
|
"The algorithm will output one feature per matching row(s) from the second vector layer." );
|
||||||
}
|
}
|
||||||
|
|
||||||
QgsJoinByAttributeAlgorithm *QgsJoinByAttributeAlgorithm::createInstance() const
|
QgsJoinByAttributeAlgorithm *QgsJoinByAttributeAlgorithm::createInstance() const
|
||||||
@ -79,6 +89,8 @@ QgsJoinByAttributeAlgorithm *QgsJoinByAttributeAlgorithm::createInstance() const
|
|||||||
|
|
||||||
QVariantMap QgsJoinByAttributeAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
|
QVariantMap QgsJoinByAttributeAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
|
||||||
{
|
{
|
||||||
|
int joinMethod = parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context );
|
||||||
|
|
||||||
std::unique_ptr< QgsFeatureSource > input( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
|
std::unique_ptr< QgsFeatureSource > input( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
|
||||||
std::unique_ptr< QgsFeatureSource > input2( parameterAsSource( parameters, QStringLiteral( "INPUT_2" ), context ) );
|
std::unique_ptr< QgsFeatureSource > input2( parameterAsSource( parameters, QStringLiteral( "INPUT_2" ), context ) );
|
||||||
if ( !input || !input2 )
|
if ( !input || !input2 )
|
||||||
@ -129,7 +141,7 @@ QVariantMap QgsJoinByAttributeAlgorithm::processAlgorithm( const QVariantMap &pa
|
|||||||
|
|
||||||
|
|
||||||
// cache attributes of input2
|
// cache attributes of input2
|
||||||
QHash< QVariant, QgsAttributes > input2AttributeCache;
|
QMultiHash< QVariant, QgsAttributes > input2AttributeCache;
|
||||||
QgsFeatureIterator features = input2->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( fields2Fetch ) );
|
QgsFeatureIterator features = input2->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( fields2Fetch ) );
|
||||||
double step = input2->featureCount() > 0 ? 50.0 / input2->featureCount() : 1;
|
double step = input2->featureCount() > 0 ? 50.0 / input2->featureCount() : 1;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -144,7 +156,7 @@ QVariantMap QgsJoinByAttributeAlgorithm::processAlgorithm( const QVariantMap &pa
|
|||||||
|
|
||||||
feedback->setProgress( i * step );
|
feedback->setProgress( i * step );
|
||||||
|
|
||||||
if ( input2AttributeCache.contains( feat.attribute( joinField2Index ) ) )
|
if ( joinMethod == 0 && input2AttributeCache.contains( feat.attribute( joinField2Index ) ) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// only keep selected attributes
|
// only keep selected attributes
|
||||||
@ -173,10 +185,24 @@ QVariantMap QgsJoinByAttributeAlgorithm::processAlgorithm( const QVariantMap &pa
|
|||||||
|
|
||||||
feedback->setProgress( 50 + i * step );
|
feedback->setProgress( 50 + i * step );
|
||||||
|
|
||||||
QgsAttributes attrs = feat.attributes();
|
if ( input2AttributeCache.count( feat.attribute( joinField1Index ) ) > 0 )
|
||||||
attrs.append( input2AttributeCache.value( feat.attribute( joinField1Index ) ) );
|
{
|
||||||
feat.setAttributes( attrs );
|
QgsAttributes attrs = feat.attributes();
|
||||||
sink->addFeature( feat, QgsFeatureSink::FastInsert );
|
|
||||||
|
QList< QgsAttributes > attributes = input2AttributeCache.values( feat.attribute( joinField1Index ) );
|
||||||
|
QList< QgsAttributes >::iterator attrsIt = attributes.begin();
|
||||||
|
for ( ; attrsIt != attributes.end(); ++attrsIt )
|
||||||
|
{
|
||||||
|
QgsAttributes newAttrs = attrs;
|
||||||
|
newAttrs.append( *attrsIt );
|
||||||
|
feat.setAttributes( newAttrs );
|
||||||
|
sink->addFeature( feat, QgsFeatureSink::FastInsert );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sink->addFeature( feat, QgsFeatureSink::FastInsert );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap outputs;
|
QVariantMap outputs;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user