Add method to collate all used time ranges for layers in project

Unlike the existing methods, this new method does not return
a single overall time range, but rather (possibly with gaps)
containing all time ranges were we know data actually exists
in the project.
This commit is contained in:
Nyall Dawson 2021-03-24 13:34:51 +10:00
parent b7c1c3af4c
commit cae8aae849
10 changed files with 133 additions and 7 deletions

View File

@ -81,6 +81,17 @@ Sets the layers temporal settings to appropriate defaults based on
a provider's temporal ``capabilities``.
%End
virtual QList< QgsDateTimeRange > allTemporalRanges( QgsMapLayer *layer ) const;
%Docstring
Attempts to calculate the overall list of all temporal extents which are contained in the specified ``layer``, using
the settings defined by the temporal properties object.
May return an empty list if the ranges could not be calculated.
.. versionadded:: 3.20
%End
};
/************************************************************************

View File

@ -29,6 +29,19 @@ Calculates the temporal range for a ``project``.
This method considers the temporal range available from layers contained within the project and
returns the maximal combined temporal extent of these layers.
%End
static QList< QgsDateTimeRange > usedTemporalRangesForProject( QgsProject *project );
%Docstring
Calculates all temporal ranges which are in use for a ``project``.
This method considers the temporal range available from layers contained within the project and
returns a list of ranges which cover only the temporal ranges which are actually in use by layers
in the project.
The returned list may be non-contiguous and have gaps in the ranges. The ranges are sorted in ascending order.
.. versionadded:: 3.20
%End
struct AnimationExportSettings

View File

@ -32,6 +32,8 @@ The ``enabled`` argument specifies whether the temporal properties are initially
virtual bool isVisibleInTemporalRange( const QgsDateTimeRange &range ) const;
virtual QList< QgsDateTimeRange > allTemporalRanges( QgsMapLayer *layer ) const;
enum TemporalMode
{

View File

@ -31,3 +31,8 @@ QgsDateTimeRange QgsMapLayerTemporalProperties::calculateTemporalExtent( QgsMapL
{
return QgsDateTimeRange();
}
QList<QgsDateTimeRange> QgsMapLayerTemporalProperties::allTemporalRanges( QgsMapLayer *layer ) const
{
return { calculateTemporalExtent( layer ) };
}

View File

@ -117,6 +117,17 @@ class CORE_EXPORT QgsMapLayerTemporalProperties : public QgsTemporalProperty
*/
virtual QgsDateTimeRange calculateTemporalExtent( QgsMapLayer *layer ) const;
#endif
/**
* Attempts to calculate the overall list of all temporal extents which are contained in the specified \a layer, using
* the settings defined by the temporal properties object.
*
* May return an empty list if the ranges could not be calculated.
*
* \since QGIS 3.20
*/
virtual QList< QgsDateTimeRange > allTemporalRanges( QgsMapLayer *layer ) const;
};
#endif // QGSMAPLAYERTEMPORALPROPERTIES_H

View File

@ -30,15 +30,13 @@
QgsDateTimeRange QgsTemporalUtils::calculateTemporalRangeForProject( QgsProject *project )
{
const QMap<QString, QgsMapLayer *> &mapLayers = project->mapLayers();
QgsMapLayer *currentLayer = nullptr;
QMap<QString, QgsMapLayer *> mapLayers = project->mapLayers();
QDateTime minDate;
QDateTime maxDate;
for ( QMap<QString, QgsMapLayer *>::const_iterator it = mapLayers.constBegin(); it != mapLayers.constEnd(); ++it )
for ( auto it = mapLayers.constBegin(); it != mapLayers.constEnd(); ++it )
{
currentLayer = it.value();
QgsMapLayer *currentLayer = it.value();
if ( !currentLayer->temporalProperties() || !currentLayer->temporalProperties()->isActive() )
continue;
@ -53,6 +51,24 @@ QgsDateTimeRange QgsTemporalUtils::calculateTemporalRangeForProject( QgsProject
return QgsDateTimeRange( minDate, maxDate );
}
QList< QgsDateTimeRange > QgsTemporalUtils::usedTemporalRangesForProject( QgsProject *project )
{
QMap<QString, QgsMapLayer *> mapLayers = project->mapLayers();
QList< QgsDateTimeRange > ranges;
for ( auto it = mapLayers.constBegin(); it != mapLayers.constEnd(); ++it )
{
QgsMapLayer *currentLayer = it.value();
if ( !currentLayer->temporalProperties() || !currentLayer->temporalProperties()->isActive() )
continue;
ranges.append( currentLayer->temporalProperties()->allTemporalRanges( currentLayer ) );
}
return QgsDateTimeRange::mergeRanges( ranges );
}
bool QgsTemporalUtils::exportAnimation( const QgsMapSettings &mapSettings, const QgsTemporalUtils::AnimationExportSettings &settings, QString &error, QgsFeedback *feedback )
{
if ( settings.fileNameTemplate.isEmpty() )

View File

@ -45,6 +45,19 @@ class CORE_EXPORT QgsTemporalUtils
*/
static QgsDateTimeRange calculateTemporalRangeForProject( QgsProject *project );
/**
* Calculates all temporal ranges which are in use for a \a project.
*
* This method considers the temporal range available from layers contained within the project and
* returns a list of ranges which cover only the temporal ranges which are actually in use by layers
* in the project.
*
* The returned list may be non-contiguous and have gaps in the ranges. The ranges are sorted in ascending order.
*
* \since QGIS 3.20
*/
static QList< QgsDateTimeRange > usedTemporalRangesForProject( QgsProject *project );
//! Contains settings relating to exporting animations
struct AnimationExportSettings
{

View File

@ -58,6 +58,27 @@ QgsDateTimeRange QgsRasterLayerTemporalProperties::calculateTemporalExtent( QgsM
return QgsDateTimeRange();
}
QList<QgsDateTimeRange> QgsRasterLayerTemporalProperties::allTemporalRanges( QgsMapLayer *layer ) const
{
QgsRasterLayer *rasterLayer = qobject_cast< QgsRasterLayer *>( layer );
if ( !rasterLayer )
return {};
switch ( mMode )
{
case QgsRasterLayerTemporalProperties::ModeFixedTemporalRange:
return { mFixedRange };
case QgsRasterLayerTemporalProperties::ModeTemporalRangeFromDataProvider:
{
QList< QgsDateTimeRange > ranges = rasterLayer->dataProvider()->temporalCapabilities()->allAvailableTemporalRanges();
return ranges.empty() ? QList< QgsDateTimeRange > { rasterLayer->dataProvider()->temporalCapabilities()->availableTemporalRange() } : ranges;
}
}
return {};
}
QgsRasterLayerTemporalProperties::TemporalMode QgsRasterLayerTemporalProperties::mode() const
{
return mMode;

View File

@ -47,6 +47,7 @@ class CORE_EXPORT QgsRasterLayerTemporalProperties : public QgsMapLayerTemporalP
bool isVisibleInTemporalRange( const QgsDateTimeRange &range ) const override;
QgsDateTimeRange calculateTemporalExtent( QgsMapLayer *layer ) const override SIP_SKIP;
QList< QgsDateTimeRange > allTemporalRanges( QgsMapLayer *layer ) const override;
/**
* Mode of the raster temporal properties

View File

@ -16,9 +16,9 @@ from qgis.core import (QgsProject,
QgsTemporalUtils,
QgsRasterLayer,
QgsDateTimeRange,
QgsDateTimeRange,
QgsInterval,
QgsUnitTypes)
QgsUnitTypes,
QgsRasterLayerTemporalProperties)
from qgis.PyQt.QtCore import (QDate,
QTime,
@ -55,6 +55,39 @@ class TestQgsTemporalUtils(unittest.TestCase):
self.assertEqual(range.begin(), QDateTime(QDate(2019, 1, 1), QTime(), Qt.UTC))
self.assertEqual(range.end(), QDateTime(QDate(2020, 7, 31), QTime(), Qt.UTC))
def testUsedTemporalRangesForProject(self):
p = QgsProject()
r1 = QgsRasterLayer('', '', 'wms')
r2 = QgsRasterLayer('', '', 'wms')
r3 = QgsRasterLayer('', '', 'wms')
r4 = QgsRasterLayer('', '', 'wms')
r1.temporalProperties().setIsActive(True)
r1.temporalProperties().setMode(QgsRasterLayerTemporalProperties.ModeTemporalRangeFromDataProvider)
r1.dataProvider().temporalCapabilities().setAvailableTemporalRange(QgsDateTimeRange(QDateTime(QDate(2020, 1, 1), QTime(), Qt.UTC),
QDateTime(QDate(2020, 3, 31), QTime(), Qt.UTC)))
r2.temporalProperties().setIsActive(True)
r2.temporalProperties().setMode(QgsRasterLayerTemporalProperties.ModeTemporalRangeFromDataProvider)
r2.dataProvider().temporalCapabilities().setAllAvailableTemporalRanges([QgsDateTimeRange(QDateTime(QDate(2020, 4, 1), QTime(), Qt.UTC),
QDateTime(QDate(2020, 7, 31), QTime(), Qt.UTC))])
r3.temporalProperties().setIsActive(True)
r3.temporalProperties().setMode(QgsRasterLayerTemporalProperties.ModeTemporalRangeFromDataProvider)
r3.dataProvider().temporalCapabilities().setAllAvailableTemporalRanges([QgsDateTimeRange(QDateTime(QDate(2019, 1, 1), QTime(), Qt.UTC),
QDateTime(QDate(2020, 2, 28), QTime(), Qt.UTC))])
r4.temporalProperties().setIsActive(True)
r4.temporalProperties().setMode(QgsRasterLayerTemporalProperties.ModeTemporalRangeFromDataProvider)
r4.dataProvider().temporalCapabilities().setAllAvailableTemporalRanges([QgsDateTimeRange(QDateTime(QDate(2021, 1, 1), QTime(), Qt.UTC),
QDateTime(QDate(2021, 2, 28), QTime(), Qt.UTC))])
p.addMapLayers([r1, r2, r3, r4])
ranges = QgsTemporalUtils.usedTemporalRangesForProject(p)
self.assertEqual(ranges, [QgsDateTimeRange(QDateTime(QDate(2019, 1, 1), QTime(), Qt.UTC),
QDateTime(QDate(2020, 3, 31), QTime(), Qt.UTC)),
QgsDateTimeRange(QDateTime(QDate(2020, 4, 1), QTime(), Qt.UTC),
QDateTime(QDate(2020, 7, 31), QTime(), Qt.UTC)),
QgsDateTimeRange(QDateTime(QDate(2021, 1, 1), QTime(), Qt.UTC),
QDateTime(QDate(2021, 2, 28), QTime(), Qt.UTC))])
def testFrameTimeCalculation(self):
expected = {QgsUnitTypes.TemporalMilliseconds: QDateTime(QDate(2021, 1, 1), QTime(12, 0, 0, 10), Qt.UTC),
QgsUnitTypes.TemporalSeconds: QDateTime(QDate(2021, 1, 1), QTime(12, 0, 10, 0), Qt.UTC),