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 Subclasses which override this method should take care to also call the base
class method in order to write common metadata properties. 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 %End


protected: 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 bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const;


virtual void combine( const QgsAbstractMetadataBase *other );



bool operator==( const QgsLayerMetadata &metadataOther ) const; 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 bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const;


virtual void combine( const QgsAbstractMetadataBase *other );



bool operator==( const QgsProjectMetadata &metadataOther ) const; 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; 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 bool QgsAbstractMetadataBase::equals( const QgsAbstractMetadataBase &metadataOther ) const
{ {
return ( ( mIdentifier == metadataOther.mIdentifier ) && 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; 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: 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; 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 const QgsLayerMetadata::Extent &QgsLayerMetadata::extent() const
{ {
return mExtent; 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 readMetadataXml( const QDomElement &metadataElement ) override;
bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const override; bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const override;
void combine( const QgsAbstractMetadataBase *other ) override;


bool operator==( const QgsLayerMetadata &metadataOther ) const; 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; 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 bool QgsProjectMetadata::operator==( const QgsProjectMetadata &metadataOther ) const
{ {
return equals( metadataOther ) && 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 readMetadataXml( const QDomElement &metadataElement ) override;
bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const override; bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const override;
void combine( const QgsAbstractMetadataBase *other ) override;


bool operator==( const QgsProjectMetadata &metadataOther ) const; 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].section, 'links')
self.assertEqual(list[0].identifier, 0) 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__': if __name__ == '__main__':
unittest.main() unittest.main()

0 comments on commit b4f1090

Please sign in to comment.