Skip to content

Commit d98defe

Browse files
committedMay 8, 2020
[feature] Add basic temporal handling support for vector layers
This exposes some basic temporal capabilities for vector layers: - static time range for layer (to match raster layer possibilities), this sets a single static time range which applies to the whole layer. ALL features from the layer will be shown whenever the canvas time overlaps the layer time range - "Single field with datetime": Allows selection of a single date or datetime field from the layer. Features will be shown whenever this field value is within the canvas time range - "Separate Fields for Start and End Date/Time": Allows selection of start and end date/datetime fields from the layer. Features will be shown whenever the time interval calculated from these fields overlaps the canvas time range Some known limitations/inefficiencies: - currently only date/datetime fields can be used. This was done to simplify the format handling and avoid the need to worry about string fields with different datetime formats. In future we should allow selection of string fields and allow users to enter a custom datetime format string - unlike the Time Manager plugin approach, the approach taken here is to rely completely on QGIS expressions and feature requests to do the filtering (Time Manager uses layer filter strings and attempts to set a native SQL filter syntax so that filtering is done on the backend). This is intentional, because it provides a unified filter approach regardless of the provider used (i.e. we don't need to worry about the different SQL syntaxes used natively by the different providers). The beauty of feature request expression compilation **should** mean that the QGIS expressions are magically turned into native backend queries, BUUUUUUUUUUUT... because we lack QGIS expression support for date time literals, we currently rely on the "to_datetime" expression function and coerce everything through strings. None of the expression compilers handle this function, so currently ALL filtering is done on the QGIS side. We need to add functions for optimised datetime literal creation, and then ensure that the different compilers correctly map these literals across to the backend filter syntax to allow all the filtering work to be done on the database side... So currently, performance is much worse with large layers compared to Time Manager. But, the advantage is that we can use the native temporal framework and have vector layers animated alongside mesh and raster layers!
1 parent 66760b4 commit d98defe

30 files changed

+1321
-61
lines changed
 
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/core/qgsvectorlayertemporalproperties.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
13+
14+
class QgsVectorLayerTemporalProperties : QgsMapLayerTemporalProperties
15+
{
16+
%Docstring
17+
Implementation of map layer temporal properties for vector layers.
18+
19+
.. versionadded:: 3.14
20+
%End
21+
22+
%TypeHeaderCode
23+
#include "qgsvectorlayertemporalproperties.h"
24+
%End
25+
public:
26+
27+
QgsVectorLayerTemporalProperties( QObject *parent /TransferThis/ = 0, bool enabled = false );
28+
%Docstring
29+
Constructor for QgsVectorLayerTemporalProperties, with the specified ``parent`` object.
30+
31+
The ``enabled`` argument specifies whether the temporal properties are initially enabled or not (see isActive()).
32+
%End
33+
34+
virtual bool isVisibleInTemporalRange( const QgsDateTimeRange &range ) const;
35+
36+
37+
enum TemporalMode
38+
{
39+
ModeFixedTemporalRange,
40+
ModeFeatureDateTimeInstantFromField,
41+
ModeFeatureDateTimeStartAndEndFromFields,
42+
};
43+
44+
TemporalMode mode() const;
45+
%Docstring
46+
Returns the temporal properties mode.
47+
48+
.. seealso:: :py:func:`setMode`
49+
%End
50+
51+
void setMode( TemporalMode mode );
52+
%Docstring
53+
Sets the temporal properties ``mode``.
54+
55+
.. seealso:: :py:func:`mode`
56+
%End
57+
58+
void setFixedTemporalRange( const QgsDateTimeRange &range );
59+
%Docstring
60+
Sets a temporal ``range`` to apply to the whole layer. All features from
61+
the layer will be rendered whenever the current datetime range of
62+
a render context intersects the specified ``range``.
63+
64+
.. warning::
65+
66+
This setting is only effective when mode() is
67+
QgsVectorLayerTemporalProperties.ModeFixedTemporalRange
68+
69+
.. seealso:: :py:func:`fixedTemporalRange`
70+
%End
71+
72+
const QgsDateTimeRange &fixedTemporalRange() const;
73+
%Docstring
74+
Returns the fixed temporal range for the layer.
75+
76+
.. warning::
77+
78+
To be used only when mode() is
79+
QgsVectorLayerTemporalProperties.ModeFixedTemporalRange
80+
81+
.. seealso:: :py:func:`setFixedTemporalRange`
82+
%End
83+
84+
QString startField() const;
85+
%Docstring
86+
Returns the name of the start datetime field, which
87+
contains the start time for the feature's time spans.
88+
89+
If mode() is ModeFeatureDateTimeInstantFromField, then this field
90+
represents both the start AND end times.
91+
92+
.. seealso:: :py:func:`setStartField`
93+
94+
.. seealso:: :py:func:`endField`
95+
%End
96+
97+
void setStartField( const QString &field );
98+
%Docstring
99+
Sets the name of the start datetime ``field``, which
100+
contains the start time for the feature's time spans.
101+
102+
If mode() is ModeFeatureDateTimeInstantFromField, then this field
103+
represents both the start AND end times.
104+
105+
.. seealso:: :py:func:`startField`
106+
107+
.. seealso:: :py:func:`setEndField`
108+
%End
109+
110+
QString endField() const;
111+
%Docstring
112+
Returns the name of the end datetime field, which
113+
contains the end time for the feature's time spans.
114+
115+
.. seealso:: :py:func:`setEndField`
116+
117+
.. seealso:: :py:func:`startField`
118+
%End
119+
120+
void setEndField( const QString &field );
121+
%Docstring
122+
Sets the name of the end datetime ``field``, which
123+
contains the end time for the feature's time spans.
124+
125+
.. seealso:: :py:func:`endField`
126+
127+
.. seealso:: :py:func:`setStartField`
128+
%End
129+
130+
QString createFilterString( QgsVectorLayer *layer, const QgsDateTimeRange &range ) const;
131+
%Docstring
132+
Creates a QGIS expression filter string for filtering features from ``layer``
133+
to those within the specified time ``range``.
134+
135+
The returned expression string considers the mode() and other related
136+
settings (such as startField()) when building the filter string.
137+
138+
.. warning::
139+
140+
Note that ModeFixedTemporalRange is intentional NOT handled by this method
141+
and if mode() is ModeFixedTemporalRange then an empty string will be returned. Use
142+
isVisibleInTemporalRange() when testing whether features from a layer set to the
143+
ModeFixedTemporalRange should ALL be filtered out.
144+
%End
145+
146+
virtual QDomElement writeXml( QDomElement &element, QDomDocument &doc, const QgsReadWriteContext &context );
147+
148+
virtual bool readXml( const QDomElement &element, const QgsReadWriteContext &context );
149+
150+
virtual void setDefaultsFromDataProviderTemporalCapabilities( const QgsDataProviderTemporalCapabilities *capabilities );
151+
152+
153+
};
154+
155+
/************************************************************************
156+
* This file has been generated automatically from *
157+
* *
158+
* src/core/qgsvectorlayertemporalproperties.h *
159+
* *
160+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
161+
************************************************************************/

‎python/core/core_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@
236236
%Include auto_generated/qgsvectorlayerjoinbuffer.sip
237237
%Include auto_generated/qgsvectorlayerjoininfo.sip
238238
%Include auto_generated/qgsvectorlayerserverproperties.sip
239+
%Include auto_generated/qgsvectorlayertemporalproperties.sip
239240
%Include auto_generated/qgsvectorlayertools.sip
240241
%Include auto_generated/qgsvectorlayerundocommand.sip
241242
%Include auto_generated/qgsvectorlayerundopassthroughcommand.sip

‎python/gui/auto_generated/qgsoptionsdialogbase.sip.in

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ Resizes all tabs when the dialog is resized
7878
bool iconOnly();
7979
%Docstring
8080
Determine if the options list is in icon only mode
81+
%End
82+
83+
void setCurrentPage( const QString &page );
84+
%Docstring
85+
Sets the dialog ``page`` (by object name) to show.
86+
87+
.. versionadded:: 3.14
8188
%End
8289

8390
public slots:
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/qgsvectorlayertemporalpropertieswidget.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
13+
class QgsVectorLayerTemporalPropertiesWidget : QWidget
14+
{
15+
%Docstring
16+
A widget for configuring the temporal properties for a vector layer.
17+
18+
.. versionadded:: 3.14
19+
%End
20+
21+
%TypeHeaderCode
22+
#include "qgsvectorlayertemporalpropertieswidget.h"
23+
%End
24+
public:
25+
26+
QgsVectorLayerTemporalPropertiesWidget( QWidget *parent = 0, QgsVectorLayer *layer = 0 );
27+
%Docstring
28+
Constructor for QgsVectorLayerTemporalPropertiesWidget.
29+
%End
30+
31+
void saveTemporalProperties();
32+
%Docstring
33+
Save widget temporal properties inputs.
34+
%End
35+
36+
};
37+
/************************************************************************
38+
* This file has been generated automatically from *
39+
* *
40+
* src/gui/qgsvectorlayertemporalpropertieswidget.h *
41+
* *
42+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
43+
************************************************************************/

‎python/gui/auto_generated/raster/qgsrasterlayerproperties.sip.in

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,6 @@ Constructor
3434
:param canvas: the QgsMapCanvas instance
3535
:param parent: the parent of this widget
3636
:param fl: windows flag
37-
%End
38-
39-
void setCurrentPage( const QString &page );
40-
%Docstring
41-
Sets the dialog ``page`` (by object name) to show.
42-
43-
.. versionadded:: 3.14
4437
%End
4538

4639
protected slots:

‎python/gui/gui_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@
215215
%Include auto_generated/qgsuserinputwidget.sip
216216
%Include auto_generated/qgsvaliditycheckresultswidget.sip
217217
%Include auto_generated/qgsvariableeditorwidget.sip
218+
%Include auto_generated/qgsvectorlayertemporalpropertieswidget.sip
218219
%Include auto_generated/qgsvertexmarker.sip
219220
%Include auto_generated/qgsvscrollarea.sip
220221
%Include auto_generated/qgswindowmanagerinterface.sip

‎src/app/qgisapp.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15366,6 +15366,9 @@ void QgisApp::showLayerProperties( QgsMapLayer *mapLayer, const QString &page )
1536615366
case QgsMapLayerType::MeshLayer:
1536715367
{
1536815368
QgsMeshLayerProperties meshLayerPropertiesDialog( mapLayer, mMapCanvas, this );
15369+
if ( !page.isEmpty() )
15370+
meshLayerPropertiesDialog.setCurrentPage( page );
15371+
1536915372
mMapStyleWidget->blockUpdates( true );
1537015373
if ( meshLayerPropertiesDialog.exec() )
1537115374
{
@@ -15400,6 +15403,9 @@ void QgisApp::showLayerProperties( QgsMapLayer *mapLayer, const QString &page )
1540015403
vectorLayerPropertiesDialog->addPropertiesPageFactory( factory );
1540115404
}
1540215405

15406+
if ( !page.isEmpty() )
15407+
vectorLayerPropertiesDialog->setCurrentPage( page );
15408+
1540315409
mMapStyleWidget->blockUpdates( true );
1540415410
if ( vectorLayerPropertiesDialog->exec() )
1540515411
{

‎src/app/qgslayertreeviewtemporalindicator.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,14 @@ void QgsLayerTreeViewTemporalIndicatorProvider::onIndicatorClicked( const QModel
5151
switch ( layer->type() )
5252
{
5353
case QgsMapLayerType::RasterLayer:
54-
QgisApp::instance()->showLayerProperties( qobject_cast<QgsRasterLayer *>( layer ), QStringLiteral( "mOptsPage_Temporal" ) );
54+
QgisApp::instance()->showLayerProperties( layer, QStringLiteral( "mOptsPage_Temporal" ) );
5555
break;
5656
case QgsMapLayerType::MeshLayer:
57-
QgisApp::instance()->showLayerProperties( qobject_cast<QgsMeshLayer *>( layer ), QStringLiteral( "mOptsPage_Temporal" ) );
57+
QgisApp::instance()->showLayerProperties( layer, QStringLiteral( "mOptsPage_Temporal" ) );
5858
break;
5959
case QgsMapLayerType::VectorLayer:
60+
QgisApp::instance()->showLayerProperties( layer, QStringLiteral( "mOptsPage_Temporal" ) );
61+
break;
6062
case QgsMapLayerType::PluginLayer:
6163
case QgsMapLayerType::VectorTileLayer:
6264
break;

‎src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ SET(QGIS_CORE_SRCS
432432
qgsvectorlayerjoinbuffer.cpp
433433
qgsvectorlayerjoininfo.cpp
434434
qgsvectorlayerrenderer.cpp
435+
qgsvectorlayertemporalproperties.cpp
435436
qgsvectorlayertools.cpp
436437
qgsvectorlayerundocommand.cpp
437438
qgsvectorlayerundopassthroughcommand.cpp
@@ -994,6 +995,7 @@ SET(QGIS_CORE_HDRS
994995
qgsvectorlayerjoininfo.h
995996
qgsvectorlayerrenderer.h
996997
qgsvectorlayerserverproperties.h
998+
qgsvectorlayertemporalproperties.h
997999
qgsvectorlayertools.h
9981000
qgsvectorlayerundocommand.h
9991001
qgsvectorlayerundopassthroughcommand.h

‎src/core/qgstemporalutils.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include "qgsmaplayertemporalproperties.h"
1919
#include "qgsrasterlayer.h"
2020
#include "qgsmeshlayer.h"
21+
#include "qgsvectorlayer.h"
22+
#include "qgsvectorlayertemporalproperties.h"
2123

2224
QgsDateTimeRange QgsTemporalUtils::calculateTemporalRangeForProject( QgsProject *project )
2325
{
@@ -51,15 +53,57 @@ QgsDateTimeRange QgsTemporalUtils::calculateTemporalRangeForProject( QgsProject
5153
layerRange = rasterLayer->dataProvider()->temporalCapabilities()->availableTemporalRange();
5254
break;
5355
}
56+
break;
57+
}
58+
59+
case QgsMapLayerType::VectorLayer:
60+
{
61+
QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer );
62+
63+
QgsVectorLayerTemporalProperties *properties = static_cast< QgsVectorLayerTemporalProperties * >( vectorLayer->temporalProperties() );
64+
switch ( properties->mode() )
65+
{
66+
case QgsVectorLayerTemporalProperties::ModeFixedTemporalRange:
67+
layerRange = properties->fixedTemporalRange();
68+
break;
69+
70+
case QgsVectorLayerTemporalProperties::ModeFeatureDateTimeInstantFromField:
71+
{
72+
const int fieldIndex = vectorLayer->fields().lookupField( properties->startField() );
73+
if ( fieldIndex >= 0 )
74+
{
75+
layerRange = QgsDateTimeRange( vectorLayer->minimumValue( fieldIndex ).toDateTime(),
76+
vectorLayer->maximumValue( fieldIndex ).toDateTime() );
77+
}
78+
break;
79+
}
80+
81+
case QgsVectorLayerTemporalProperties::ModeFeatureDateTimeStartAndEndFromFields:
82+
{
83+
const int startFieldIndex = vectorLayer->fields().lookupField( properties->startField() );
84+
const int endFieldIndex = vectorLayer->fields().lookupField( properties->endField() );
85+
if ( startFieldIndex >= 0 && endFieldIndex >= 0 )
86+
{
87+
layerRange = QgsDateTimeRange( std::min( vectorLayer->minimumValue( startFieldIndex ).toDateTime(),
88+
vectorLayer->minimumValue( endFieldIndex ).toDateTime() ),
89+
std::max( vectorLayer->maximumValue( startFieldIndex ).toDateTime(),
90+
vectorLayer->maximumValue( endFieldIndex ).toDateTime() ) );
91+
}
92+
break;
93+
}
94+
}
95+
break;
5496
}
55-
break;
97+
5698
case QgsMapLayerType::MeshLayer:
5799
{
58100
QgsMeshLayer *meshLayer = qobject_cast<QgsMeshLayer *>( currentLayer );
59101
layerRange = meshLayer->temporalProperties()->timeExtent();
102+
break;
60103
}
61-
break;
62-
default:
104+
105+
case QgsMapLayerType::PluginLayer:
106+
case QgsMapLayerType::VectorTileLayer:
63107
break;
64108
}
65109

0 commit comments

Comments
 (0)