Skip to content

Commit

Permalink
Add method to combine two QGIS metadata objects
Browse files Browse the repository at this point in the history
All non-empty elements from one metadata object are copied onto
the target metadata object
  • Loading branch information
nyalldawson committed May 4, 2021
1 parent c27e22f commit b4f1090
Show file tree
Hide file tree
Showing 12 changed files with 544 additions and 0 deletions.
Expand Up @@ -424,6 +424,15 @@ Stores state in a DOM node.

Subclasses which override this method should take care to also call the base
class method in order to write common metadata properties.
%End

virtual void combine( const QgsAbstractMetadataBase *other );
%Docstring
Combines the metadata from this object with the metadata from an ``other`` object.

Any existing values in this object will be overwritten by non-empty values from ``other``.

.. versionadded:: 3.20
%End

protected:
Expand Down
2 changes: 2 additions & 0 deletions python/core/auto_generated/metadata/qgslayermetadata.sip.in
Expand Up @@ -275,6 +275,8 @@ Reads the metadata state from a ``layer``'s custom properties (see :py:func:`Qgs

virtual bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const;

virtual void combine( const QgsAbstractMetadataBase *other );


bool operator==( const QgsLayerMetadata &metadataOther ) const;

Expand Down
2 changes: 2 additions & 0 deletions python/core/auto_generated/metadata/qgsprojectmetadata.sip.in
Expand Up @@ -82,6 +82,8 @@ Sets the project's creation date/timestamp.

virtual bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const;

virtual void combine( const QgsAbstractMetadataBase *other );


bool operator==( const QgsProjectMetadata &metadataOther ) const;

Expand Down
36 changes: 36 additions & 0 deletions src/core/metadata/qgsabstractmetadatabase.cpp
Expand Up @@ -432,6 +432,42 @@ bool QgsAbstractMetadataBase::writeMetadataXml( QDomElement &metadataElement, QD
return true;
}

void QgsAbstractMetadataBase::combine( const QgsAbstractMetadataBase *other )
{
if ( !other )
return;

if ( !other->identifier().isEmpty() )
mIdentifier = other->identifier();

if ( !other->parentIdentifier().isEmpty() )
mParentIdentifier = other->parentIdentifier();

if ( !other->language().isEmpty() )
mLanguage = other->language();

if ( !other->type().isEmpty() )
mType = other->type();

if ( !other->title().isEmpty() )
mTitle = other->title();

if ( !other->abstract().isEmpty() )
mAbstract = other->abstract();

if ( !other->history().isEmpty() )
mHistory = other->history();

if ( !other->keywords().isEmpty() )
mKeywords = other->keywords();

if ( !other->contacts().isEmpty() )
mContacts = other->contacts();

if ( !other->links().isEmpty() )
mLinks = other->links();
}

bool QgsAbstractMetadataBase::equals( const QgsAbstractMetadataBase &metadataOther ) const
{
return ( ( mIdentifier == metadataOther.mIdentifier ) &&
Expand Down
9 changes: 9 additions & 0 deletions src/core/metadata/qgsabstractmetadatabase.h
Expand Up @@ -524,6 +524,15 @@ class CORE_EXPORT QgsAbstractMetadataBase
*/
virtual bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const;

/**
* Combines the metadata from this object with the metadata from an \a other object.
*
* Any existing values in this object will be overwritten by non-empty values from \a other.
*
* \since QGIS 3.20
*/
virtual void combine( const QgsAbstractMetadataBase *other );

protected:

/**
Expand Down
32 changes: 32 additions & 0 deletions src/core/metadata/qgslayermetadata.cpp
Expand Up @@ -341,6 +341,38 @@ bool QgsLayerMetadata::writeMetadataXml( QDomElement &metadataElement, QDomDocum
return true;
}

void QgsLayerMetadata::combine( const QgsAbstractMetadataBase *other )
{
QgsAbstractMetadataBase::combine( other );

if ( const QgsLayerMetadata *otherLayerMetadata = dynamic_cast< const QgsLayerMetadata * >( other ) )
{
if ( !otherLayerMetadata->fees().isEmpty() )
mFees = otherLayerMetadata->fees();

if ( !otherLayerMetadata->constraints().isEmpty() )
mConstraints = otherLayerMetadata->constraints();

if ( !otherLayerMetadata->rights().isEmpty() )
mRights = otherLayerMetadata->rights();

if ( !otherLayerMetadata->licenses().isEmpty() )
mLicenses = otherLayerMetadata->licenses();

if ( !otherLayerMetadata->encoding().isEmpty() )
mEncoding = otherLayerMetadata->encoding();

if ( otherLayerMetadata->crs().isValid() )
mCrs = otherLayerMetadata->crs();

if ( !otherLayerMetadata->extent().spatialExtents().isEmpty() )
mExtent.setSpatialExtents( otherLayerMetadata->extent().spatialExtents() );

if ( !otherLayerMetadata->extent().temporalExtents().isEmpty() )
mExtent.setTemporalExtents( otherLayerMetadata->extent().temporalExtents() );
}
}

const QgsLayerMetadata::Extent &QgsLayerMetadata::extent() const
{
return mExtent;
Expand Down
1 change: 1 addition & 0 deletions src/core/metadata/qgslayermetadata.h
Expand Up @@ -315,6 +315,7 @@ class CORE_EXPORT QgsLayerMetadata : public QgsAbstractMetadataBase

bool readMetadataXml( const QDomElement &metadataElement ) override;
bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const override;
void combine( const QgsAbstractMetadataBase *other ) override;

bool operator==( const QgsLayerMetadata &metadataOther ) const;

Expand Down
14 changes: 14 additions & 0 deletions src/core/metadata/qgsprojectmetadata.cpp
Expand Up @@ -55,6 +55,20 @@ bool QgsProjectMetadata::writeMetadataXml( QDomElement &metadataElement, QDomDoc
return true;
}

void QgsProjectMetadata::combine( const QgsAbstractMetadataBase *other )
{
QgsAbstractMetadataBase::combine( other );

if ( const QgsProjectMetadata *otherProjectMetadata = dynamic_cast< const QgsProjectMetadata * >( other ) )
{
if ( !otherProjectMetadata->author().isEmpty() )
mAuthor = otherProjectMetadata->author();

if ( otherProjectMetadata->creationDateTime().isValid() )
mCreationDateTime = otherProjectMetadata->creationDateTime();
}
}

bool QgsProjectMetadata::operator==( const QgsProjectMetadata &metadataOther ) const
{
return equals( metadataOther ) &&
Expand Down
1 change: 1 addition & 0 deletions src/core/metadata/qgsprojectmetadata.h
Expand Up @@ -87,6 +87,7 @@ class CORE_EXPORT QgsProjectMetadata : public QgsAbstractMetadataBase

bool readMetadataXml( const QDomElement &metadataElement ) override;
bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const override;
void combine( const QgsAbstractMetadataBase *other ) override;

bool operator==( const QgsProjectMetadata &metadataOther ) const;

Expand Down
196 changes: 196 additions & 0 deletions tests/src/python/test_qgslayermetadata.py
Expand Up @@ -481,6 +481,202 @@ def testValidateNative(self): # spellok
self.assertEqual(list[0].section, 'links')
self.assertEqual(list[0].identifier, 0)

def testCombine(self):
m1 = QgsLayerMetadata()
m2 = QgsLayerMetadata()

# should be retained
m1.setIdentifier('i1')
m1.combine(m2)
self.assertEqual(m1.identifier(), 'i1')

# should be overwritten
m1.setIdentifier(None)
m2.setIdentifier('i2')
m1.combine(m2)
self.assertEqual(m1.identifier(), 'i2')

# should be overwritten
m1.setIdentifier('i1')
m2.setIdentifier('i2')
m1.combine(m2)
self.assertEqual(m1.identifier(), 'i2')

m1.setParentIdentifier('pi1')
m2.setParentIdentifier(None)
m1.combine(m2)
self.assertEqual(m1.parentIdentifier(), 'pi1')

m1.setParentIdentifier(None)
m2.setParentIdentifier('pi2')
m1.combine(m2)
self.assertEqual(m1.parentIdentifier(), 'pi2')

m1.setLanguage('l1')
m2.setLanguage(None)
m1.combine(m2)
self.assertEqual(m1.language(), 'l1')

m1.setLanguage(None)
m2.setLanguage('l2')
m1.combine(m2)
self.assertEqual(m1.language(), 'l2')

m1.setType('ty1')
m2.setType(None)
m1.combine(m2)
self.assertEqual(m1.type(), 'ty1')

m1.setType(None)
m2.setType('ty2')
m1.combine(m2)
self.assertEqual(m1.type(), 'ty2')

m1.setTitle('t1')
m2.setTitle(None)
m1.combine(m2)
self.assertEqual(m1.title(), 't1')

m1.setTitle(None)
m2.setTitle('t2')
m1.combine(m2)
self.assertEqual(m1.title(), 't2')

m1.setAbstract('a1')
m2.setAbstract(None)
m1.combine(m2)
self.assertEqual(m1.abstract(), 'a1')

m1.setAbstract(None)
m2.setAbstract('a2')
m1.combine(m2)
self.assertEqual(m1.abstract(), 'a2')

m1.setHistory(['h1', 'hh1'])
m2.setHistory([])
m1.combine(m2)
self.assertEqual(m1.history(), ['h1', 'hh1'])

m1.setHistory([])
m2.setHistory(['h2', 'hh2'])
m1.combine(m2)
self.assertEqual(m1.history(), ['h2', 'hh2'])

m1.setKeywords({'words': ['k1', 'kk1']})
m2.setKeywords({})
m1.combine(m2)
self.assertEqual(m1.keywords(), {'words': ['k1', 'kk1']})

m1.setKeywords({})
m2.setKeywords({'words': ['k2', 'kk2']})
m1.combine(m2)
self.assertEqual(m1.keywords(), {'words': ['k2', 'kk2']})

m1.setContacts([QgsLayerMetadata.Contact('c1'), QgsLayerMetadata.Contact('cc1')])
m2.setContacts([])
m1.combine(m2)
self.assertEqual(m1.contacts(), [QgsLayerMetadata.Contact('c1'), QgsLayerMetadata.Contact('cc1')])

m1.setContacts([])
m2.setContacts([QgsLayerMetadata.Contact('c2'), QgsLayerMetadata.Contact('cc2')])
m1.combine(m2)
self.assertEqual(m1.contacts(), [QgsLayerMetadata.Contact('c2'), QgsLayerMetadata.Contact('cc2')])

m1.setLinks([QgsLayerMetadata.Link('l1'), QgsLayerMetadata.Link('ll1')])
m2.setLinks([])
m1.combine(m2)
self.assertEqual(m1.links(), [QgsLayerMetadata.Link('l1'), QgsLayerMetadata.Link('ll1')])

m1.setLinks([])
m2.setLinks([QgsLayerMetadata.Link('l2'), QgsLayerMetadata.Link('ll2')])
m1.combine(m2)
self.assertEqual(m1.links(), [QgsLayerMetadata.Link('l2'), QgsLayerMetadata.Link('ll2')])

m1.setFees('f1')
m2.setFees(None)
m1.combine(m2)
self.assertEqual(m1.fees(), 'f1')

m1.setFees(None)
m2.setFees('f2')
m1.combine(m2)
self.assertEqual(m1.fees(), 'f2')

m1.setConstraints([QgsLayerMetadata.Constraint('c1'), QgsLayerMetadata.Constraint('cc1')])
m2.setConstraints([])
m1.combine(m2)
self.assertEqual(m1.constraints(), [QgsLayerMetadata.Constraint('c1'), QgsLayerMetadata.Constraint('cc1')])

m1.setConstraints([])
m2.setConstraints([QgsLayerMetadata.Constraint('c2'), QgsLayerMetadata.Constraint('cc2')])
m1.combine(m2)
self.assertEqual(m1.constraints(), [QgsLayerMetadata.Constraint('c2'), QgsLayerMetadata.Constraint('cc2')])

m1.setRights(['r1', 'rr1'])
m2.setRights([])
m1.combine(m2)
self.assertEqual(m1.rights(), ['r1', 'rr1'])

m1.setRights([])
m2.setRights(['r2', 'rr2'])
m1.combine(m2)
self.assertEqual(m1.rights(), ['r2', 'rr2'])

m1.setLicenses(['li1', 'lli1'])
m2.setLicenses([])
m1.combine(m2)
self.assertEqual(m1.licenses(), ['li1', 'lli1'])

m1.setLicenses([])
m2.setLicenses(['li2', 'lli2'])
m1.combine(m2)
self.assertEqual(m1.licenses(), ['li2', 'lli2'])

m1.setEncoding('e1')
m2.setEncoding(None)
m1.combine(m2)
self.assertEqual(m1.encoding(), 'e1')

m1.setEncoding(None)
m2.setEncoding('e2')
m1.combine(m2)
self.assertEqual(m1.encoding(), 'e2')

m1.setCrs(QgsCoordinateReferenceSystem('EPSG:3111'))
m2.setCrs(QgsCoordinateReferenceSystem())
m1.combine(m2)
self.assertEqual(m1.crs().authid(), 'EPSG:3111')

m1.setCrs(QgsCoordinateReferenceSystem())
m2.setCrs(QgsCoordinateReferenceSystem('EPSG:3113'))
m1.combine(m2)
self.assertEqual(m1.crs().authid(), 'EPSG:3113')

s = QgsLayerMetadata.SpatialExtent()
s.bounds = QgsBox3d(1, 2, 3, 4, 5, 6)
m1.extent().setSpatialExtents([s])
m2.extent().setSpatialExtents([])
m1.combine(m2)
self.assertEqual(m1.extent().spatialExtents()[0].bounds, QgsBox3d(1, 2, 3, 4, 5, 6))

s.bounds = QgsBox3d(11, 12, 13, 14, 15, 16)
m1.extent().setSpatialExtents([])
m2.extent().setSpatialExtents([s])
m1.combine(m2)
self.assertEqual(m1.extent().spatialExtents()[0].bounds, QgsBox3d(11, 12, 13, 14, 15, 16))

s = QgsDateTimeRange(QDateTime(2020, 1, 1, 0, 0, 0), QDateTime(2020, 2, 1, 0, 0, 0))
m1.extent().setTemporalExtents([s])
m2.extent().setTemporalExtents([])
m1.combine(m2)
self.assertEqual(m1.extent().temporalExtents()[0], s)

s = QgsDateTimeRange(QDateTime(2021, 1, 1, 0, 0, 0), QDateTime(2021, 2, 1, 0, 0, 0))
m1.extent().setTemporalExtents([])
m2.extent().setTemporalExtents([s])
m1.combine(m2)
self.assertEqual(m1.extent().temporalExtents()[0], s)


if __name__ == '__main__':
unittest.main()

0 comments on commit b4f1090

Please sign in to comment.