From 3d4f6f82bbbc525ea6cb797e6397bb6d588abca6 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 20 Feb 2018 09:59:39 +1000 Subject: [PATCH] Followup 0548f4, fixes to layer metadata and metadata widget Address outstanding review requests so that this PR can be merged --- python/core/geometry/qgsbox3d.sip.in | 2 + python/core/metadata/qgslayermetadata.sip.in | 14 ++ python/core/qgsdataprovider.sip.in | 18 +- python/core/qgsrange.sip.in | 2 + python/core/qgsvectordataprovider.sip.in | 2 + .../core/raster/qgsrasterdataprovider.sip.in | 21 +++ python/gui/qgsmetadatawidget.sip.in | 29 +--- src/core/geometry/qgsbox3d.cpp | 7 + src/core/geometry/qgsbox3d.h | 2 + src/core/metadata/qgslayermetadata.cpp | 85 +++++++--- src/core/metadata/qgslayermetadata.h | 18 +- src/core/qgsdataprovider.h | 26 +-- src/core/qgsrange.h | 8 + src/core/qgsvectordataprovider.h | 2 + src/core/qgsvectorlayer.cpp | 7 +- src/core/raster/qgsrasterdataprovider.cpp | 5 + src/core/raster/qgsrasterdataprovider.h | 23 +++ src/core/raster/qgsrasterlayer.cpp | 7 +- src/gui/qgsmetadatawidget.cpp | 49 +++--- src/gui/qgsmetadatawidget.h | 41 ++--- tests/src/python/test_qgsbox3d.py | 11 ++ tests/src/python/test_qgslayermetadata.py | 155 ++++++++++++++++++ tests/src/python/test_qgsrange.py | 8 + 23 files changed, 424 insertions(+), 118 deletions(-) diff --git a/python/core/geometry/qgsbox3d.sip.in b/python/core/geometry/qgsbox3d.sip.in index 15489d356cb..a565fbafe12 100644 --- a/python/core/geometry/qgsbox3d.sip.in +++ b/python/core/geometry/qgsbox3d.sip.in @@ -223,6 +223,8 @@ will be performed on the x/y extent of the box only. Converts the box to a 2D rectangle. %End + bool operator==( const QgsBox3d &other ) const; + }; /************************************************************************ diff --git a/python/core/metadata/qgslayermetadata.sip.in b/python/core/metadata/qgslayermetadata.sip.in index db95ea090c8..400dcea96e4 100644 --- a/python/core/metadata/qgslayermetadata.sip.in +++ b/python/core/metadata/qgslayermetadata.sip.in @@ -50,6 +50,8 @@ using QgsNativeMetadataValidator. QgsCoordinateReferenceSystem extentCrs; QgsBox3d bounds; + + bool operator==( const QgsLayerMetadata::SpatialExtent &other ) const; }; struct Extent @@ -87,6 +89,8 @@ Sets the temporal ``extents`` of the resource. .. seealso:: :py:func:`temporalExtents` %End + bool operator==( const Extent &other ) const; + }; @@ -101,6 +105,9 @@ Constructor for Constraint. QString type; QString constraint; + + bool operator==( const QgsLayerMetadata::Constraint &other ) const; + }; typedef QList< QgsLayerMetadata::Constraint > ConstraintList; @@ -125,6 +132,8 @@ Constructor for Address. QString postalCode; QString country; + + bool operator==( const QgsLayerMetadata::Address &other ) const; }; struct Contact @@ -150,6 +159,8 @@ Constructor for Contact. QString email; QString role; + + bool operator==( const QgsLayerMetadata::Contact &other ) const; }; typedef QList< QgsLayerMetadata::Contact > ContactList; @@ -176,6 +187,8 @@ Constructor for Link. QString mimeType; QString size; + + bool operator==( const QgsLayerMetadata::Link &other ) const; }; typedef QList< QgsLayerMetadata::Link > LinkList; @@ -618,6 +631,7 @@ Stores state in Dom node :return: true if successful %End + bool operator==( const QgsLayerMetadata &metadataOther ) const; }; diff --git a/python/core/qgsdataprovider.sip.in b/python/core/qgsdataprovider.sip.in index 886c669bb20..2c6f7317470 100644 --- a/python/core/qgsdataprovider.sip.in +++ b/python/core/qgsdataprovider.sip.in @@ -364,19 +364,21 @@ The default implementation does nothing. virtual QgsLayerMetadata layerMetadata() const; %Docstring -Retrieve collected Metadata from the Provider source - A structured metadata store for a map layer. -\note +Returns layer metadata collected from the provider's source. -.. seealso:: :py:func:`setLayerMetadata` +Individual data providers must implement this method if they support collecting metadata. + +.. seealso:: :py:func:`writeLayerMetadata` .. versionadded:: 3.0 %End - virtual bool setLayerMetadata( const QgsLayerMetadata &layerMetadata ); + + virtual bool writeLayerMetadata( const QgsLayerMetadata &metadata ); %Docstring -Set collected Metadata from the Provider source - A structured metadata store for a map layer. -\note +Writes layer ``metadata`` to the underlying provider source. Support depends +on individual provider capabilities. + +Returns true if metadata was successfully written to the data provider. .. seealso:: :py:func:`layerMetadata` diff --git a/python/core/qgsrange.sip.in b/python/core/qgsrange.sip.in index 5188c6dd0e0..8b007b8c760 100644 --- a/python/core/qgsrange.sip.in +++ b/python/core/qgsrange.sip.in @@ -243,6 +243,8 @@ Returns true if this range contains a specified ``element``. Returns true if this range overlaps another range. %End + bool operator==( const QgsTemporalRange &other ) const; + }; diff --git a/python/core/qgsvectordataprovider.sip.in b/python/core/qgsvectordataprovider.sip.in index 6aef97c0cdd..82d21f5b513 100644 --- a/python/core/qgsvectordataprovider.sip.in +++ b/python/core/qgsvectordataprovider.sip.in @@ -48,6 +48,8 @@ of feature and attribute information from a spatial datasource. ChangeFeatures, RenameAttributes, FastTruncate, + ReadLayerMetadata, + WriteLayerMetadata, }; typedef QFlags Capabilities; diff --git a/python/core/raster/qgsrasterdataprovider.sip.in b/python/core/raster/qgsrasterdataprovider.sip.in index 31c6283de11..cbc64e30cc1 100644 --- a/python/core/raster/qgsrasterdataprovider.sip.in +++ b/python/core/raster/qgsrasterdataprovider.sip.in @@ -69,6 +69,17 @@ Base class for raster data providers. #include "qgsrasterdataprovider.h" %End public: + + enum ProviderCapability + { + NoProviderCapabilities, + ReadLayerMetadata, + WriteLayerMetadata, + }; + + typedef QFlags ProviderCapabilities; + + QgsRasterDataProvider(); QgsRasterDataProvider( const QString &uri ); @@ -76,6 +87,13 @@ Base class for raster data providers. virtual QgsRasterInterface *clone() const = 0; + virtual QgsRasterDataProvider::ProviderCapabilities providerCapabilities() const; +%Docstring +Returns flags containing the supported capabilities of the data provider. + +.. versionadded:: 3.0 +%End + virtual bool setInput( QgsRasterInterface *input ); %Docstring It makes no sense to set input on provider */ @@ -425,6 +443,9 @@ Copy member variables from other raster data provider. Useful for implementation }; +QFlags operator|(QgsRasterDataProvider::ProviderCapability f1, QFlags f2); + + /************************************************************************ * This file has been generated automatically from * diff --git a/python/gui/qgsmetadatawidget.sip.in b/python/gui/qgsmetadatawidget.sip.in index 0b735df5f94..46384acb35c 100644 --- a/python/gui/qgsmetadatawidget.sip.in +++ b/python/gui/qgsmetadatawidget.sip.in @@ -26,30 +26,26 @@ class QgsMetadataWidget : QWidget QgsMetadataWidget( QWidget *parent, QgsMapLayer *layer = 0 ); %Docstring Constructor for the wizard. -\note -For use with a source \layer. This constructor automatically sets the widget's metadata() if the ``layer`` pointer is valid. -calls setMetadata, using mMetadata -:param layer: to set the main QgsLayerMetadata with mLayer->metadata() when not None +If ``layer`` is set, then this constructor automatically sets the widget's metadata() to match +the layer's metadata.. .. seealso:: :py:func:`setMetadata` %End - void setMetadata( const QgsLayerMetadata &layerMetadata ); + void setMetadata( const QgsLayerMetadata &metadata ); %Docstring -Sets the ``metadata`` to display in the widget -\note -Called from constructor and initializes child widget on first use -Can be called from outside to change the QgsLayerMetadata object. +Sets the ``metadata`` to display in the widget. + +This method can be called after constructing a QgsMetadataWidget in order +to set the displayed metadata to custom, non-layer based metadata. .. seealso:: :py:func:`metadata` %End QgsLayerMetadata metadata(); %Docstring -Retrieves a QgsLayerMetadata object representing the current state of the widget. -\note -saveMetdata is called before returning :py:class:`QgsLayerMetadata` +Returns a QgsLayerMetadata object representing the current state of the widget. .. seealso:: :py:func:`saveMetadata` %End @@ -58,7 +54,7 @@ saveMetdata is called before returning :py:class:`QgsLayerMetadata` %Docstring Save all fields in a QgsLayerMetadata object. -.. seealso:: :py:func:`getMetadata` +.. seealso:: :py:func:`metadata` .. seealso:: :py:func:`acceptMetadata` @@ -69,8 +65,6 @@ Save all fields in a QgsLayerMetadata object. %Docstring Check if values in the wizard are correct. -.. seealso:: :py:func:`updatePanel` - .. seealso:: :py:func:`saveMetadata` %End @@ -82,11 +76,6 @@ If the CRS is updated. void acceptMetadata(); %Docstring Saves the metadata to the layer. -%End - - virtual void setMetadata( const QgsLayerMetadata &metadata ); -%Docstring -Sets the layer's ``metadata`` store. %End static QMap parseLanguages(); diff --git a/src/core/geometry/qgsbox3d.cpp b/src/core/geometry/qgsbox3d.cpp index 03007e5d502..19f40dbe5a5 100644 --- a/src/core/geometry/qgsbox3d.cpp +++ b/src/core/geometry/qgsbox3d.cpp @@ -117,3 +117,10 @@ bool QgsBox3d::contains( const QgsPoint &p ) const else return true; } + +bool QgsBox3d::operator==( const QgsBox3d &other ) const +{ + return mBounds2d == other.mBounds2d && + qgsDoubleNear( mZmin, other.mZmin ) && + qgsDoubleNear( mZmax, other.mZmax ); +} diff --git a/src/core/geometry/qgsbox3d.h b/src/core/geometry/qgsbox3d.h index 8ba6222ebd6..32086233339 100644 --- a/src/core/geometry/qgsbox3d.h +++ b/src/core/geometry/qgsbox3d.h @@ -202,6 +202,8 @@ class CORE_EXPORT QgsBox3d */ QgsRectangle toRectangle() const { return mBounds2d; } + bool operator==( const QgsBox3d &other ) const; + private: QgsRectangle mBounds2d; diff --git a/src/core/metadata/qgslayermetadata.cpp b/src/core/metadata/qgslayermetadata.cpp index e0ac0e0d7a6..233d0d107a4 100644 --- a/src/core/metadata/qgslayermetadata.cpp +++ b/src/core/metadata/qgslayermetadata.cpp @@ -768,25 +768,72 @@ void QgsLayerMetadata::Extent::setTemporalExtents( const QList mTemporalExtents = temporalExtents; } +bool QgsLayerMetadata::Extent::operator==( const QgsLayerMetadata::Extent &other ) const +{ + return mSpatialExtents == other.mSpatialExtents && mTemporalExtents == other.mTemporalExtents; +} + bool QgsLayerMetadata::operator==( const QgsLayerMetadata &metadataOther ) const { - return ( ( parentIdentifier() == metadataOther.parentIdentifier() ) && - ( identifier() == metadataOther.identifier() ) && - ( language() == metadataOther.language() ) && - ( type() == metadataOther.type() ) && - ( title() == metadataOther.title() ) && - ( abstract() == metadataOther.abstract() ) && - ( fees() == metadataOther.fees() ) && - ( rights() == metadataOther.rights() ) && - ( licenses() == metadataOther.licenses() ) && - ( history() == metadataOther.history() ) && - ( encoding() == metadataOther.encoding() ) && - ( crs() == metadataOther.crs() ) && - ( keywords() == metadataOther.keywords() ) && - ( categories() == metadataOther.categories() ) && - // QgsLayerMetadata::ConstraintList, LinkList, Extent, ContactList need to be delt with properly - ( constraints().count() == metadataOther.constraints().count() ) && - ( extent().spatialExtents().count() == metadataOther.extent().spatialExtents().count() ) && - ( contacts().count() == metadataOther.contacts().count() ) && - ( links().count() == metadataOther.links().count() ) ); + return ( ( mIdentifier == metadataOther.mIdentifier ) && + ( mParentIdentifier == metadataOther.mParentIdentifier ) && + ( mLanguage == metadataOther.mLanguage ) && + ( mType == metadataOther.mType ) && + ( mTitle == metadataOther.mTitle ) && + ( mAbstract == metadataOther.mAbstract ) && + ( mFees == metadataOther.mFees ) && + ( mConstraints == metadataOther.mConstraints ) && + ( mRights == metadataOther.mRights ) && + ( mLicenses == metadataOther.mLicenses ) && + ( mHistory == metadataOther.mHistory ) && + ( mEncoding == metadataOther.mEncoding ) && + ( mCrs == metadataOther.mCrs ) && + ( mExtent == metadataOther.mExtent ) && + ( mKeywords == metadataOther.mKeywords ) && + ( mContacts == metadataOther.mContacts ) && + ( mLinks == metadataOther.mLinks ) ); +} + +bool QgsLayerMetadata::SpatialExtent::operator==( const QgsLayerMetadata::SpatialExtent &other ) const +{ + return extentCrs == other.extentCrs && + bounds == other.bounds; +} + +bool QgsLayerMetadata::Constraint::operator==( const QgsLayerMetadata::Constraint &other ) const +{ + return type == other.type && constraint == other.constraint; +} + +bool QgsLayerMetadata::Contact::operator==( const QgsLayerMetadata::Contact &other ) const +{ + return name == other.name && + organization == other.organization && + position == other.position && + addresses == other.addresses && + voice == other.voice && + fax == other.fax && + email == other.email && + role == other.role; +} + +bool QgsLayerMetadata::Link::operator==( const QgsLayerMetadata::Link &other ) const +{ + return name == other.name && + type == other.type && + description == other.description && + url == other.url && + format == other.format && + mimeType == other.mimeType && + size == other.size; +} + +bool QgsLayerMetadata::Address::operator==( const QgsLayerMetadata::Address &other ) const +{ + return type == other.type && + address == other.address && + city == other.city && + administrativeArea == other.administrativeArea && + postalCode == other.postalCode && + country == other.country; } diff --git a/src/core/metadata/qgslayermetadata.h b/src/core/metadata/qgslayermetadata.h index 322f631e01d..45009c29fcf 100644 --- a/src/core/metadata/qgslayermetadata.h +++ b/src/core/metadata/qgslayermetadata.h @@ -84,6 +84,8 @@ class CORE_EXPORT QgsLayerMetadata * \see extentCrs */ QgsBox3d bounds; + + bool operator==( const QgsLayerMetadata::SpatialExtent &other ) const; }; /** @@ -120,6 +122,8 @@ class CORE_EXPORT QgsLayerMetadata */ void setTemporalExtents( const QList< QgsDateTimeRange > &extents ); + bool operator==( const Extent &other ) const; + #ifndef SIP_RUN private: @@ -154,6 +158,9 @@ class CORE_EXPORT QgsLayerMetadata * Free-form constraint string. */ QString constraint; + + bool operator==( const QgsLayerMetadata::Constraint &other ) const; + }; /** @@ -209,6 +216,8 @@ class CORE_EXPORT QgsLayerMetadata * Free-form country string. */ QString country; + + bool operator==( const QgsLayerMetadata::Address &other ) const; }; /** @@ -266,6 +275,8 @@ class CORE_EXPORT QgsLayerMetadata * E.g. 'custodian', 'owner', 'distributor', etc. */ QString role; + + bool operator==( const QgsLayerMetadata::Contact &other ) const; }; /** @@ -324,6 +335,8 @@ class CORE_EXPORT QgsLayerMetadata * Estimated size (in bytes) of the online resource response. */ QString size; + + bool operator==( const QgsLayerMetadata::Link &other ) const; }; /** @@ -726,10 +739,7 @@ class CORE_EXPORT QgsLayerMetadata */ bool writeMetadataXml( QDomElement &metadataElement, QDomDocument &document ) const; - /** - * Compares two features - */ - bool operator==( const QgsLayerMetadata &metadataOther ) const SIP_SKIP; + bool operator==( const QgsLayerMetadata &metadataOther ) const; private: diff --git a/src/core/qgsdataprovider.h b/src/core/qgsdataprovider.h index c725b505c90..6047cedd2a6 100644 --- a/src/core/qgsdataprovider.h +++ b/src/core/qgsdataprovider.h @@ -494,22 +494,24 @@ class CORE_EXPORT QgsDataProvider : public QObject virtual bool renderInPreview( const QgsDataProvider::PreviewContext &context ); // SIP_SKIP /** - * Retrieve collected Metadata from the Provider source - * \brief A structured metadata store for a map layer. - * \note - * \see setLayerMetadata - * \since QGIS 3.0 + * Returns layer metadata collected from the provider's source. + * + * Individual data providers must implement this method if they support collecting metadata. + * + * \see writeLayerMetadata() + * \since QGIS 3.0 */ - virtual QgsLayerMetadata layerMetadata() const { return QgsLayerMetadata(); }; + virtual QgsLayerMetadata layerMetadata() const { return QgsLayerMetadata(); } /** - * Set collected Metadata from the Provider source - * \brief A structured metadata store for a map layer. - * \note - * \see layerMetadata - * \since QGIS 3.0 + * Writes layer \a metadata to the underlying provider source. Support depends + * on individual provider capabilities. + * + * Returns true if metadata was successfully written to the data provider. + * \see layerMetadata() + * \since QGIS 3.0 */ - virtual bool setLayerMetadata( const QgsLayerMetadata &layerMetadata ) { Q_UNUSED( layerMetadata ); return false; } + virtual bool writeLayerMetadata( const QgsLayerMetadata &metadata ) { Q_UNUSED( metadata ); return false; } signals: diff --git a/src/core/qgsrange.h b/src/core/qgsrange.h index 324cb7e9ad4..ee3e108376c 100644 --- a/src/core/qgsrange.h +++ b/src/core/qgsrange.h @@ -393,6 +393,14 @@ class QgsTemporalRange return false; } + bool operator==( const QgsTemporalRange &other ) const + { + return mLower == other.mLower && + mUpper == other.mUpper && + mIncludeLower == other.mIncludeLower && + mIncludeUpper == other.mIncludeUpper; + } + private: T mLower; diff --git a/src/core/qgsvectordataprovider.h b/src/core/qgsvectordataprovider.h index c226743107c..bec7b2a44f7 100644 --- a/src/core/qgsvectordataprovider.h +++ b/src/core/qgsvectordataprovider.h @@ -88,6 +88,8 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat * ChangeGeometries | ChangeAttributeValues */ RenameAttributes = 1 << 19, //!< Supports renaming attributes (fields). Since QGIS 2.16 FastTruncate = 1 << 20, //!< Supports fast truncation of the layer (removing all features). Since QGIS 3.0 + ReadLayerMetadata = 1 << 21, //!< Provider can read layer metadata from data store. Since QGIS 3.0. See QgsDataProvider::layerMetadata() + WriteLayerMetadata = 1 << 22, //!< Provider can write layer metadata to the data store. Since QGIS 3.0. See QgsDataProvider::writeLayerMetadata() }; Q_DECLARE_FLAGS( Capabilities, Capability ) diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index ba7474b70b8..591278b80a3 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -1526,8 +1526,11 @@ bool QgsVectorLayer::setDataProvider( QString const &provider ) return false; } - setMetadata( mDataProvider->layerMetadata() ); - QgsDebugMsgLevel( QString( "Set Data provider QgsLayerMetadata identifier[%1]" ).arg( metadata().identifier() ), 4 ); + if ( mDataProvider->capabilities() & QgsVectorDataProvider::ReadLayerMetadata ) + { + setMetadata( mDataProvider->layerMetadata() ); + QgsDebugMsgLevel( QString( "Set Data provider QgsLayerMetadata identifier[%1]" ).arg( metadata().identifier() ), 4 ); + } // TODO: Check if the provider has the capability to send fullExtentCalculated connect( mDataProvider, &QgsVectorDataProvider::fullExtentCalculated, this, [ = ] { updateExtents(); } ); diff --git a/src/core/raster/qgsrasterdataprovider.cpp b/src/core/raster/qgsrasterdataprovider.cpp index bbd73246537..5c5ab0ad1de 100644 --- a/src/core/raster/qgsrasterdataprovider.cpp +++ b/src/core/raster/qgsrasterdataprovider.cpp @@ -216,6 +216,11 @@ QgsRasterDataProvider::QgsRasterDataProvider( QString const &uri ) { } +QgsRasterDataProvider::ProviderCapabilities QgsRasterDataProvider::providerCapabilities() const +{ + return QgsRasterDataProvider::NoProviderCapabilities; +} + // //Random Static convenience function // diff --git a/src/core/raster/qgsrasterdataprovider.h b/src/core/raster/qgsrasterdataprovider.h index 336aec2f67c..959c3aa04fb 100644 --- a/src/core/raster/qgsrasterdataprovider.h +++ b/src/core/raster/qgsrasterdataprovider.h @@ -87,12 +87,33 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast Q_OBJECT public: + + /** + * Enumeration with capabilities that raster providers might implement. + * \since QGIS 3.0 + */ + enum ProviderCapability + { + NoProviderCapabilities = 0, //!< Provider has no capabilities + ReadLayerMetadata = 1 << 1, //!< Provider can read layer metadata from data store. Since QGIS 3.0. See QgsDataProvider::layerMetadata() + WriteLayerMetadata = 1 << 2, //!< Provider can write layer metadata to the data store. Since QGIS 3.0. See QgsDataProvider::writeLayerMetadata() + }; + + //! Provider capabilities + Q_DECLARE_FLAGS( ProviderCapabilities, ProviderCapability ) + QgsRasterDataProvider(); QgsRasterDataProvider( const QString &uri ); QgsRasterInterface *clone() const override = 0; + /** + * Returns flags containing the supported capabilities of the data provider. + * \since QGIS 3.0 + */ + virtual QgsRasterDataProvider::ProviderCapabilities providerCapabilities() const; + /* It makes no sense to set input on provider */ bool setInput( QgsRasterInterface *input ) override { Q_UNUSED( input ); return false; } @@ -540,6 +561,8 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast }; +Q_DECLARE_OPERATORS_FOR_FLAGS( QgsRasterDataProvider::ProviderCapabilities ) + // clazy:excludeall=qstring-allocations #endif diff --git a/src/core/raster/qgsrasterlayer.cpp b/src/core/raster/qgsrasterlayer.cpp index c84e73cfc4a..5dc0a39ec83 100644 --- a/src/core/raster/qgsrasterlayer.cpp +++ b/src/core/raster/qgsrasterlayer.cpp @@ -625,8 +625,11 @@ void QgsRasterLayer::setDataProvider( QString const &provider ) return; } - setMetadata( mDataProvider->layerMetadata() ); - QgsDebugMsgLevel( QString( "Set Data provider QgsLayerMetadata identifier[%1]" ).arg( metadata().identifier() ), 4 ); + if ( mDataProvider->providerCapabilities() & QgsRasterDataProvider::ReadLayerMetadata ) + { + setMetadata( mDataProvider->layerMetadata() ); + QgsDebugMsgLevel( QString( "Set Data provider QgsLayerMetadata identifier[%1]" ).arg( metadata().identifier() ), 4 ); + } if ( provider == QLatin1String( "gdal" ) ) { diff --git a/src/gui/qgsmetadatawidget.cpp b/src/gui/qgsmetadatawidget.cpp index 81270de1d42..402dc7edc76 100644 --- a/src/gui/qgsmetadatawidget.cpp +++ b/src/gui/qgsmetadatawidget.cpp @@ -114,7 +114,7 @@ QgsMetadataWidget::QgsMetadataWidget( QWidget *parent, QgsMapLayer *layer ) { btnAutoSource->setEnabled( false ); btnAutoEncoding->setEnabled( false ); - btnAutoCrs->setEnabled( false ); + btnSetCrsFromLayer->setEnabled( false ); } setMetadata( mMetadata ); } @@ -127,11 +127,12 @@ void QgsMetadataWidget::setMetadata( const QgsLayerMetadata &layerMetadata ) QgsLayerMetadata QgsMetadataWidget::metadata() { - saveMetadata( mMetadata ); - return mMetadata; + QgsLayerMetadata md; + saveMetadata( md ); + return md; } -void QgsMetadataWidget::fillSourceFromLayer() const +void QgsMetadataWidget::fillSourceFromLayer() { if ( mLayer ) { @@ -139,7 +140,7 @@ void QgsMetadataWidget::fillSourceFromLayer() const } } -void QgsMetadataWidget::addVocabulary() const +void QgsMetadataWidget::addVocabulary() { int row = tabKeywords->rowCount(); tabKeywords->setRowCount( row + 1 ); @@ -154,7 +155,7 @@ void QgsMetadataWidget::addVocabulary() const tabKeywords->setItem( row, 1, pCell ); } -void QgsMetadataWidget::removeSelectedVocabulary() const +void QgsMetadataWidget::removeSelectedVocabulary() { QItemSelectionModel *selectionModel = tabKeywords->selectionModel(); const QModelIndexList selectedRows = selectionModel->selectedRows(); @@ -176,7 +177,7 @@ void QgsMetadataWidget::addLicence() } } -void QgsMetadataWidget::removeSelectedLicence() const +void QgsMetadataWidget::removeSelectedLicence() { QItemSelectionModel *selectionModel = tabLicenses->selectionModel(); const QModelIndexList selectedRows = selectionModel->selectedRows(); @@ -197,7 +198,7 @@ void QgsMetadataWidget::addRight() } } -void QgsMetadataWidget::removeSelectedRight() const +void QgsMetadataWidget::removeSelectedRight() { QItemSelectionModel *selection = listRights->selectionModel(); if ( selection->hasSelection() ) @@ -211,20 +212,20 @@ void QgsMetadataWidget::removeSelectedRight() const } } -void QgsMetadataWidget::addConstraint() const +void QgsMetadataWidget::addConstraint() { int row = mConstraintsModel->rowCount(); mConstraintsModel->setItem( row, 0, new QStandardItem( QString( tr( "undefined %1" ) ).arg( row + 1 ) ) ); mConstraintsModel->setItem( row, 1, new QStandardItem( QString( tr( "undefined %1" ) ).arg( row + 1 ) ) ); } -void QgsMetadataWidget::removeSelectedConstraint() const +void QgsMetadataWidget::removeSelectedConstraint() { const QModelIndexList selectedRows = tabConstraints->selectionModel()->selectedRows(); mConstraintsModel->removeRow( selectedRows[0].row() ); } -void QgsMetadataWidget::crsChanged() const +void QgsMetadataWidget::crsChanged() { if ( ( mCrs.isValid() ) && ( mLayer ) ) { @@ -257,7 +258,7 @@ void QgsMetadataWidget::crsChanged() const } } -void QgsMetadataWidget::addAddress() const +void QgsMetadataWidget::addAddress() { int row = tabAddresses->rowCount(); tabAddresses->setRowCount( row + 1 ); @@ -283,7 +284,7 @@ void QgsMetadataWidget::addAddress() const tabAddresses->setItem( row, 5, new QTableWidgetItem() ); } -void QgsMetadataWidget::removeSelectedAddress() const +void QgsMetadataWidget::removeSelectedAddress() { QItemSelectionModel *selectionModel = tabAddresses->selectionModel(); const QModelIndexList selectedRows = selectionModel->selectedRows(); @@ -305,7 +306,7 @@ void QgsMetadataWidget::fillCrsFromProvider() crsChanged(); } -void QgsMetadataWidget::addLink() const +void QgsMetadataWidget::addLink() { int row = mLinksModel->rowCount(); mLinksModel->setItem( row, 0, new QStandardItem( QString( tr( "undefined %1" ) ).arg( row + 1 ) ) ); @@ -317,7 +318,7 @@ void QgsMetadataWidget::addLink() const mLinksModel->setItem( row, 6, new QStandardItem() ); } -void QgsMetadataWidget::removeSelectedLink() const +void QgsMetadataWidget::removeSelectedLink() { const QModelIndexList selectedRows = tabLinks->selectionModel()->selectedRows(); mLinksModel->removeRow( selectedRows[0].row() ); @@ -334,7 +335,7 @@ void QgsMetadataWidget::addHistory() } } -void QgsMetadataWidget::removeSelectedHistory() const +void QgsMetadataWidget::removeSelectedHistory() { QItemSelectionModel *selection = listHistory->selectionModel(); if ( selection->hasSelection() ) @@ -348,7 +349,7 @@ void QgsMetadataWidget::removeSelectedHistory() const } } -void QgsMetadataWidget::fillComboBox() const +void QgsMetadataWidget::fillComboBox() { // Set default values in type combobox // It is advised to use the ISO 19115 MD_ScopeCode values. E.g. 'dataset' or 'series'. @@ -546,7 +547,7 @@ void QgsMetadataWidget::setPropertiesFromLayer() mHistoryModel->setStringList( mMetadata.history() ); } -void QgsMetadataWidget::saveMetadata( QgsLayerMetadata &layerMetadata ) const +void QgsMetadataWidget::saveMetadata( QgsLayerMetadata &layerMetadata ) { layerMetadata.setParentIdentifier( lineEditParentId->text() ); layerMetadata.setIdentifier( lineEditIdentifier->text() ); @@ -659,7 +660,7 @@ void QgsMetadataWidget::saveMetadata( QgsLayerMetadata &layerMetadata ) const layerMetadata.setHistory( mHistoryModel->stringList() ); } -bool QgsMetadataWidget::checkMetadata() const +bool QgsMetadataWidget::checkMetadata() { QgsLayerMetadata metadata = QgsLayerMetadata(); saveMetadata( metadata ); @@ -846,13 +847,7 @@ void QgsMetadataWidget::acceptMetadata() } } -void QgsMetadataWidget::setMetadata( const QgsLayerMetadata &metadata ) -{ - mMetadata = metadata; - setPropertiesFromLayer(); -} - -void QgsMetadataWidget::syncFromCategoriesTabToKeywordsTab() const +void QgsMetadataWidget::syncFromCategoriesTabToKeywordsTab() { if ( mCategoriesModel->rowCount() > 0 ) { @@ -873,7 +868,7 @@ void QgsMetadataWidget::syncFromCategoriesTabToKeywordsTab() const } } -void QgsMetadataWidget::updatePanel() const +void QgsMetadataWidget::updatePanel() { int index = tabWidget->currentIndex(); QString currentTabText = tabWidget->widget( index )->objectName(); diff --git a/src/gui/qgsmetadatawidget.h b/src/gui/qgsmetadatawidget.h index e4597608f8b..b241b4bc63a 100644 --- a/src/gui/qgsmetadatawidget.h +++ b/src/gui/qgsmetadatawidget.h @@ -43,43 +43,41 @@ class GUI_EXPORT QgsMetadataWidget : public QWidget, private Ui::QgsMetadataWidg /** * Constructor for the wizard. - * \note - * For use with a source \layer. This constructor automatically sets the widget's metadata() if the \a layer pointer is valid. - * calls setMetadata, using mMetadata - * \param layer to set the main QgsLayerMetadata with mLayer->metadata() when not nullptr - * \see setMetadata + * + * If \a layer is set, then this constructor automatically sets the widget's metadata() to match + * the layer's metadata.. + + * \see setMetadata() */ QgsMetadataWidget( QWidget *parent, QgsMapLayer *layer = nullptr ); /** - * Sets the \a metadata to display in the widget - * \note - * Called from constructor and initializes child widget on first use - * Can be called from outside to change the QgsLayerMetadata object. + * Sets the \a metadata to display in the widget. + * + * This method can be called after constructing a QgsMetadataWidget in order + * to set the displayed metadata to custom, non-layer based metadata. + * * \see metadata() */ - void setMetadata( const QgsLayerMetadata &layerMetadata ); + void setMetadata( const QgsLayerMetadata &metadata ); /** - * Retrieves a QgsLayerMetadata object representing the current state of the widget. - * \note - * saveMetdata is called before returning QgsLayerMetadata - * \see saveMetadata + * Returns a QgsLayerMetadata object representing the current state of the widget. + * \see saveMetadata() */ QgsLayerMetadata metadata(); /** * Save all fields in a QgsLayerMetadata object. - * \see getMetadata - * \see acceptMetadata - * \see checkMetadata + * \see metadata() + * \see acceptMetadata() + * \see checkMetadata() */ void saveMetadata( QgsLayerMetadata &layerMetadata ); /** * Check if values in the wizard are correct. - * \see updatePanel - * \see saveMetadata + * \see saveMetadata() */ bool checkMetadata(); @@ -93,11 +91,6 @@ class GUI_EXPORT QgsMetadataWidget : public QWidget, private Ui::QgsMetadataWidg */ void acceptMetadata(); - /** - * Sets the layer's \a metadata store. - */ - virtual void setMetadata( const QgsLayerMetadata &metadata ); - /** * Returns a list of languages available by default in the wizard. */ diff --git a/tests/src/python/test_qgsbox3d.py b/tests/src/python/test_qgsbox3d.py index 97f60aba75d..32f1e7098c0 100644 --- a/tests/src/python/test_qgsbox3d.py +++ b/tests/src/python/test_qgsbox3d.py @@ -173,6 +173,17 @@ class TestQgsBox3d(unittest.TestCase): box = QgsBox3d(5.0, 6.0, 7.0, 11.0, 13.0, -7.0) self.assertTrue(box.is2d()) + def testEquality(self): + box1 = QgsBox3d(5.0, 6.0, 7.0, 11.0, 13.0, 15.0) + box2 = QgsBox3d(5.0, 6.0, 7.0, 11.0, 13.0, 15.0) + self.assertEqual(box1, box2) + self.assertNotEqual(box1, QgsBox3d(5.0, 6.0, 7.0, 11.0, 13.0, 14.0)) + self.assertNotEqual(box1, QgsBox3d(5.0, 6.0, 7.0, 11.0, 41.0, 15.0)) + self.assertNotEqual(box1, QgsBox3d(5.0, 6.0, 7.0, 12.0, 13.0, 15.0)) + self.assertNotEqual(box1, QgsBox3d(5.0, 6.0, 17.0, 11.0, 13.0, 15.0)) + self.assertNotEqual(box1, QgsBox3d(5.0, 16.0, 7.0, 11.0, 13.0, 15.0)) + self.assertNotEqual(box1, QgsBox3d(52.0, 6.0, 7.0, 11.0, 13.0, 15.0)) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgslayermetadata.py b/tests/src/python/test_qgslayermetadata.py index 51d917de9d4..54f3d51c512 100644 --- a/tests/src/python/test_qgslayermetadata.py +++ b/tests/src/python/test_qgslayermetadata.py @@ -86,6 +86,161 @@ class TestQgsLayerMetadata(unittest.TestCase): m.setCrs(QgsCoordinateReferenceSystem.fromEpsgId(3111)) self.assertEqual(m.crs().authid(), 'EPSG:3111') + def testEquality(self): + # spatial extent + extent = QgsLayerMetadata.SpatialExtent() + extent.extentCrs = QgsCoordinateReferenceSystem.fromEpsgId(3111) + extent.bounds = QgsBox3d(5.0, 6.0, 7.0, 11.0, 13.0, 15.0) + extent2 = QgsLayerMetadata.SpatialExtent() + extent2.extentCrs = QgsCoordinateReferenceSystem.fromEpsgId(3111) + extent2.bounds = QgsBox3d(5.0, 6.0, 7.0, 11.0, 13.0, 15.0) + self.assertEqual(extent, extent2) + extent2.extentCrs = QgsCoordinateReferenceSystem.fromEpsgId(3113) + self.assertNotEqual(extent, extent2) + extent2.extentCrs = QgsCoordinateReferenceSystem.fromEpsgId(3111) + extent2.bounds = QgsBox3d(5.0, 6.0, 7.0, 11.0, 13.0, 16.0) + self.assertNotEqual(extent, extent2) + + # extent + extent = QgsLayerMetadata.Extent() + extent1 = QgsLayerMetadata.SpatialExtent() + extent1.extentCrs = QgsCoordinateReferenceSystem.fromEpsgId(3111) + extent1.bounds = QgsBox3d(5.0, 6.0, 7.0, 11.0, 13.0, 15.0) + extent2 = QgsLayerMetadata.SpatialExtent() + extent2.extentCrs = QgsCoordinateReferenceSystem.fromEpsgId(3113) + extent2.bounds = QgsBox3d(5.0, 6.0, 7.0, 11.0, 13.0, 16.0) + extent.setSpatialExtents([extent1, extent2]) + dates = [ + QgsDateTimeRange( + QDateTime(QDate(2001, 12, 17), QTime(9, 30, 47)), + QDateTime(QDate(2001, 12, 17), QTime(9, 30, 47))), + QgsDateTimeRange( + QDateTime(QDate(2010, 12, 17), QTime(9, 30, 47)), + QDateTime(QDate(2020, 12, 17), QTime(9, 30, 47))) + ] + extent.setTemporalExtents(dates) + extent_copy = QgsLayerMetadata.Extent(extent) + self.assertEqual(extent, extent_copy) + extent_copy.setTemporalExtents([ + QgsDateTimeRange( + QDateTime(QDate(2001, 12, 17), QTime(9, 30, 47)), + QDateTime(QDate(2001, 12, 17), QTime(9, 30, 47))), + QgsDateTimeRange( + QDateTime(QDate(2010, 12, 17), QTime(9, 30, 48)), + QDateTime(QDate(2020, 12, 17), QTime(9, 30, 49))) + ]) + self.assertNotEqual(extent, extent_copy) + extent_copy = QgsLayerMetadata.Extent(extent) + extent3 = QgsLayerMetadata.SpatialExtent() + extent3.extentCrs = QgsCoordinateReferenceSystem.fromEpsgId(3113) + extent3.bounds = QgsBox3d(5.0, 6.0, 7.0, 11.0, 13.0, 19.0) + extent_copy.setSpatialExtents([extent1, extent3]) + self.assertNotEqual(extent, extent_copy) + + constraint = QgsLayerMetadata.Constraint('c', 'type1') + self.assertEqual(constraint, QgsLayerMetadata.Constraint('c', 'type1')) + self.assertNotEqual(constraint, QgsLayerMetadata.Constraint('c2', 'type1')) + self.assertNotEqual(constraint, QgsLayerMetadata.Constraint('c', 'type2')) + + a = QgsLayerMetadata.Address() + a.type = 'postal' + a.address = '13 north rd' + a.city = 'huxleys haven' + a.administrativeArea = 'land of the queens' + a.postalCode = '4123' + a.country = 'straya!' + a2 = QgsLayerMetadata.Address(a) + self.assertEqual(a, a2) + a2.type = 'postal2' + self.assertNotEqual(a, a2) + a2 = QgsLayerMetadata.Address(a) + a2.address = 'address2' + self.assertNotEqual(a, a2) + a2 = QgsLayerMetadata.Address(a) + a2.city = 'city' + self.assertNotEqual(a, a2) + a2 = QgsLayerMetadata.Address(a) + a2.administrativeArea = 'area2' + self.assertNotEqual(a, a2) + a2 = QgsLayerMetadata.Address(a) + a2.postalCode = 'postal2' + self.assertNotEqual(a, a2) + a2 = QgsLayerMetadata.Address(a) + a2.country = 'country2' + self.assertNotEqual(a, a2) + + c = QgsLayerMetadata.Contact() + c.name = 'name' + c.organization = 'org' + c.position = 'pos' + c.voice = '1500 515 555' + c.fax = 'fax' + c.email = 'email' + c.role = 'role' + a = QgsLayerMetadata.Address() + a.type = 'postal' + a2 = QgsLayerMetadata.Address() + a2.type = 'street' + c.addresses = [a, a2] + c2 = QgsLayerMetadata.Contact(c) + self.assertEqual(c, c2) + c2.name = 'name2' + self.assertNotEqual(c, c2) + c2 = QgsLayerMetadata.Contact(c) + c2.organization = 'org2' + self.assertNotEqual(c, c2) + c2 = QgsLayerMetadata.Contact(c) + c2.position = 'pos2' + self.assertNotEqual(c, c2) + c2 = QgsLayerMetadata.Contact(c) + c2.voice = 'voice2' + self.assertNotEqual(c, c2) + c2 = QgsLayerMetadata.Contact(c) + c2.fax = 'fax2' + self.assertNotEqual(c, c2) + c2 = QgsLayerMetadata.Contact(c) + c2.email = 'email2' + self.assertNotEqual(c, c2) + c2 = QgsLayerMetadata.Contact(c) + c2.role = 'role2' + self.assertNotEqual(c, c2) + c2 = QgsLayerMetadata.Contact(c) + c2.addresses = [a2] + self.assertNotEqual(c, c2) + + # link + l = QgsLayerMetadata.Link() + l.name = 'name' + l.type = 'type' + l.description = 'desc' + l.url = 'url' + l.format = 'format' + l.mimeType = 'mime' + l.size = '112' + l2 = QgsLayerMetadata.Link(l) + self.assertEqual(l, l2) + l2 = QgsLayerMetadata.Link(l) + l2.name = 'name2' + self.assertNotEqual(l, l2) + l2 = QgsLayerMetadata.Link(l) + l2.type = 'type2' + self.assertNotEqual(l, l2) + l2 = QgsLayerMetadata.Link(l) + l2.description = 'desc2' + self.assertNotEqual(l, l2) + l2 = QgsLayerMetadata.Link(l) + l2.url = 'url2' + self.assertNotEqual(l, l2) + l2 = QgsLayerMetadata.Link(l) + l2.format = 'format2' + self.assertNotEqual(l, l2) + l2 = QgsLayerMetadata.Link(l) + l2.mimeType = 'mime2' + self.assertNotEqual(l, l2) + l2 = QgsLayerMetadata.Link(l) + l2.size = '113' + self.assertNotEqual(l, l2) + def testExtent(self): e = QgsLayerMetadata.Extent() se = QgsLayerMetadata.SpatialExtent() diff --git a/tests/src/python/test_qgsrange.py b/tests/src/python/test_qgsrange.py index 92827e6c0da..7a74b75c07b 100644 --- a/tests/src/python/test_qgsrange.py +++ b/tests/src/python/test_qgsrange.py @@ -348,6 +348,14 @@ class TestQgsDateRange(unittest.TestCase): self.assertTrue(QgsDateRange(QDate(2017, 3, 1), QDate(2010, 6, 2)).isEmpty()) self.assertFalse(QgsDateRange(QDate(), QDate()).isEmpty()) + def testEquality(self): + range = QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2), False, False) + self.assertEqual(range, QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2), False, False)) + self.assertNotEqual(range, QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2), False, True)) + self.assertNotEqual(range, QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2), True, False)) + self.assertNotEqual(range, QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 3), False, False)) + self.assertNotEqual(range, QgsDateRange(QDate(2010, 3, 2), QDate(2010, 6, 2), False, False)) + if __name__ == "__main__": unittest.main()