QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsvectorlayertemporalproperties.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectorlayertemporalproperties.cpp
3 ---------------
4 begin : May 2020
5 copyright : (C) 2020 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
20#include "qgsexpression.h"
21#include "qgsvectorlayer.h"
22#include "qgsfields.h"
24
26 : QgsMapLayerTemporalProperties( parent, enabled )
27{
28}
29
30bool QgsVectorLayerTemporalProperties::isVisibleInTemporalRange( const QgsDateTimeRange &range ) const
31{
32 if ( !isActive() )
33 return true;
34
35 switch ( mMode )
36 {
37 case Qgis::VectorTemporalMode::FixedTemporalRange:
38 return range.isInfinite() || mFixedRange.isInfinite() || mFixedRange.overlaps( range );
39
40 case Qgis::VectorTemporalMode::FeatureDateTimeInstantFromField:
41 case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromFields:
42 case Qgis::VectorTemporalMode::RedrawLayerOnly:
43 case Qgis::VectorTemporalMode::FeatureDateTimeStartAndDurationFromFields:
44 case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromExpressions:
45 return true;
46 }
47 return true;
48}
49
51{
52 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
53 if ( !layer )
54 return QgsDateTimeRange();
55
56 switch ( mMode )
57 {
58 case Qgis::VectorTemporalMode::FixedTemporalRange:
59 return mFixedRange;
60
61 case Qgis::VectorTemporalMode::FeatureDateTimeInstantFromField:
62 {
63 const int fieldIndex = vectorLayer->fields().lookupField( mStartFieldName );
64 if ( fieldIndex >= 0 )
65 {
66 QVariant minVal;
67 QVariant maxVal;
68 vectorLayer->minimumAndMaximumValue( fieldIndex, minVal, maxVal );
69
70 const QDateTime min = minVal.toDateTime();
71 const QDateTime maxStartTime = maxVal.toDateTime();
72 const QgsInterval eventDuration = QgsInterval( mFixedDuration, mDurationUnit );
73 return QgsDateTimeRange( min, maxStartTime + eventDuration );
74 }
75 break;
76 }
77
78 case Qgis::VectorTemporalMode::FeatureDateTimeStartAndDurationFromFields:
79 {
80 const int fieldIndex = vectorLayer->fields().lookupField( mStartFieldName );
81 const int durationFieldIndex = vectorLayer->fields().lookupField( mDurationFieldName );
82 if ( fieldIndex >= 0 && durationFieldIndex >= 0 )
83 {
84 const QDateTime minTime = vectorLayer->minimumValue( fieldIndex ).toDateTime();
85 // no choice here but to loop through all features to calculate max time :(
86
87 QgsFeature f;
88 QgsFeatureIterator it = vectorLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( QgsAttributeList() << durationFieldIndex << fieldIndex ) );
89 QDateTime maxTime;
90 while ( it.nextFeature( f ) )
91 {
92 const QDateTime start = f.attribute( fieldIndex ).toDateTime();
93 if ( start.isValid() )
94 {
95 const QVariant durationValue = f.attribute( durationFieldIndex );
96 if ( durationValue.isValid() )
97 {
98 const double duration = durationValue.toDouble();
99 const QDateTime end = start.addMSecs( QgsInterval( duration, mDurationUnit ).seconds() * 1000.0 );
100 if ( end.isValid() )
101 maxTime = maxTime.isValid() ? std::max( maxTime, end ) : end;
102 }
103 }
104 }
105 return QgsDateTimeRange( minTime, maxTime );
106 }
107 break;
108 }
109
110 case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromFields:
111 {
112 const int startFieldIndex = vectorLayer->fields().lookupField( mStartFieldName );
113 const int endFieldIndex = vectorLayer->fields().lookupField( mEndFieldName );
114 if ( startFieldIndex >= 0 && endFieldIndex >= 0 )
115 {
116 QVariant startMinVal;
117 QVariant startMaxVal;
118 vectorLayer->minimumAndMaximumValue( startFieldIndex, startMinVal, startMaxVal );
119 QVariant endMinVal;
120 QVariant endMaxVal;
121 vectorLayer->minimumAndMaximumValue( endFieldIndex, endMinVal, endMaxVal );
122
123 return QgsDateTimeRange( std::min( startMinVal.toDateTime(),
124 endMinVal.toDateTime() ),
125 std::max( startMaxVal.toDateTime(),
126 endMaxVal.toDateTime() ) );
127 }
128 else if ( startFieldIndex >= 0 )
129 {
130 QVariant startMinVal;
131 QVariant startMaxVal;
132 vectorLayer->minimumAndMaximumValue( startFieldIndex, startMinVal, startMaxVal );
133 return QgsDateTimeRange( startMinVal.toDateTime(),
134 startMaxVal.toDateTime() );
135 }
136 else if ( endFieldIndex >= 0 )
137 {
138 QVariant endMinVal;
139 QVariant endMaxVal;
140 vectorLayer->minimumAndMaximumValue( endFieldIndex, endMinVal, endMaxVal );
141 return QgsDateTimeRange( endMinVal.toDateTime(),
142 endMaxVal.toDateTime() );
143 }
144 break;
145 }
146
147 case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromExpressions:
148 {
149 const bool hasStartExpression = !mStartExpression.isEmpty();
150 const bool hasEndExpression = !mEndExpression.isEmpty();
151 if ( !hasStartExpression && !hasEndExpression )
152 return QgsDateTimeRange();
153
154 QDateTime minTime;
155 QDateTime maxTime;
156
157 // no choice here but to loop through all features
158 QgsExpressionContext context;
160
162 if ( hasStartExpression )
163 {
164 startExpression.setExpression( mStartExpression );
165 startExpression.prepare( &context );
166 }
167
169 if ( hasEndExpression )
170 {
171 endExpression.setExpression( mEndExpression );
172 endExpression.prepare( &context );
173 }
174
175 QSet< QString > fields;
176 if ( hasStartExpression )
177 fields.unite( startExpression.referencedColumns() );
178 if ( hasEndExpression )
179 fields.unite( endExpression.referencedColumns() );
180
181 const bool needsGeom = startExpression.needsGeometry() || endExpression.needsGeometry();
182
184 if ( !needsGeom )
186
187 req.setSubsetOfAttributes( fields, vectorLayer->fields() );
188
189 QgsFeature f;
190 QgsFeatureIterator it = vectorLayer->getFeatures( req );
191 while ( it.nextFeature( f ) )
192 {
193 context.setFeature( f );
194 const QDateTime start = hasStartExpression ? startExpression.evaluate( &context ).toDateTime() : QDateTime();
195 const QDateTime end = hasEndExpression ? endExpression.evaluate( &context ).toDateTime() : QDateTime();
196
197 if ( start.isValid() )
198 {
199 minTime = minTime.isValid() ? std::min( minTime, start ) : start;
200 if ( !hasEndExpression )
201 maxTime = maxTime.isValid() ? std::max( maxTime, start ) : start;
202 }
203 if ( end.isValid() )
204 {
205 maxTime = maxTime.isValid() ? std::max( maxTime, end ) : end;
206 if ( !hasStartExpression )
207 minTime = minTime.isValid() ? std::min( minTime, end ) : end;
208 }
209 }
210 return QgsDateTimeRange( minTime, maxTime );
211 }
212
213 case Qgis::VectorTemporalMode::RedrawLayerOnly:
214 break;
215 }
216
217 return QgsDateTimeRange();
218}
219
221{
222 return mMode;
223}
224
226{
227 if ( mMode == mode )
228 return;
229 mMode = mode;
230}
231
233{
234 return mLimitMode;
235}
236
238{
239 if ( mLimitMode == limitMode )
240 return;
241 mLimitMode = limitMode;
242}
243
244QgsTemporalProperty::Flags QgsVectorLayerTemporalProperties::flags() const
245{
246 return mode() == Qgis::VectorTemporalMode::FixedTemporalRange ? QgsTemporalProperty::FlagDontInvalidateCachedRendersWhenRangeChanges : QgsTemporalProperty::Flags();
247}
248
250{
251 mFixedRange = range;
252}
253
255{
256 return mFixedRange;
257}
258
259bool QgsVectorLayerTemporalProperties::readXml( const QDomElement &element, const QgsReadWriteContext &context )
260{
261 Q_UNUSED( context )
262
263 const QDomElement temporalNode = element.firstChildElement( QStringLiteral( "temporal" ) );
264
265 setIsActive( temporalNode.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt() );
266
267 mMode = static_cast< Qgis::VectorTemporalMode >( temporalNode.attribute( QStringLiteral( "mode" ), QStringLiteral( "0" ) ). toInt() );
268
269 mLimitMode = static_cast< Qgis::VectorTemporalLimitMode >( temporalNode.attribute( QStringLiteral( "limitMode" ), QStringLiteral( "0" ) ). toInt() );
270 mStartFieldName = temporalNode.attribute( QStringLiteral( "startField" ) );
271 mEndFieldName = temporalNode.attribute( QStringLiteral( "endField" ) );
272 mStartExpression = temporalNode.attribute( QStringLiteral( "startExpression" ) );
273 mEndExpression = temporalNode.attribute( QStringLiteral( "endExpression" ) );
274 mDurationFieldName = temporalNode.attribute( QStringLiteral( "durationField" ) );
275 mDurationUnit = QgsUnitTypes::decodeTemporalUnit( temporalNode.attribute( QStringLiteral( "durationUnit" ), QgsUnitTypes::encodeUnit( QgsUnitTypes::TemporalMinutes ) ) );
276 mFixedDuration = temporalNode.attribute( QStringLiteral( "fixedDuration" ) ).toDouble();
277 mAccumulateFeatures = temporalNode.attribute( QStringLiteral( "accumulate" ), QStringLiteral( "0" ) ).toInt();
278
279 const QDomNode rangeElement = temporalNode.namedItem( QStringLiteral( "fixedRange" ) );
280
281 const QDomNode begin = rangeElement.namedItem( QStringLiteral( "start" ) );
282 const QDomNode end = rangeElement.namedItem( QStringLiteral( "end" ) );
283
284 const QDateTime beginDate = QDateTime::fromString( begin.toElement().text(), Qt::ISODate );
285 const QDateTime endDate = QDateTime::fromString( end.toElement().text(), Qt::ISODate );
286
287 const QgsDateTimeRange range = QgsDateTimeRange( beginDate, endDate );
288 setFixedTemporalRange( range );
289
290 return true;
291}
292
293QDomElement QgsVectorLayerTemporalProperties::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context )
294{
295 Q_UNUSED( context )
296 if ( element.isNull() )
297 return QDomElement();
298
299 QDomElement temporalElement = document.createElement( QStringLiteral( "temporal" ) );
300 temporalElement.setAttribute( QStringLiteral( "enabled" ), isActive() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
301 temporalElement.setAttribute( QStringLiteral( "mode" ), QString::number( static_cast< int >( mMode ) ) );
302
303 temporalElement.setAttribute( QStringLiteral( "limitMode" ), QString::number( static_cast< int >( mLimitMode ) ) );
304 temporalElement.setAttribute( QStringLiteral( "startField" ), mStartFieldName );
305 temporalElement.setAttribute( QStringLiteral( "endField" ), mEndFieldName );
306 temporalElement.setAttribute( QStringLiteral( "startExpression" ), mStartExpression );
307 temporalElement.setAttribute( QStringLiteral( "endExpression" ), mEndExpression );
308 temporalElement.setAttribute( QStringLiteral( "durationField" ), mDurationFieldName );
309 temporalElement.setAttribute( QStringLiteral( "durationUnit" ), QgsUnitTypes::encodeUnit( mDurationUnit ) );
310 temporalElement.setAttribute( QStringLiteral( "fixedDuration" ), qgsDoubleToString( mFixedDuration ) );
311 temporalElement.setAttribute( QStringLiteral( "accumulate" ), mAccumulateFeatures ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
312
313 QDomElement rangeElement = document.createElement( QStringLiteral( "fixedRange" ) );
314
315 QDomElement startElement = document.createElement( QStringLiteral( "start" ) );
316 QDomElement endElement = document.createElement( QStringLiteral( "end" ) );
317
318 const QDomText startText = document.createTextNode( mFixedRange.begin().toTimeSpec( Qt::OffsetFromUTC ).toString( Qt::ISODate ) );
319 const QDomText endText = document.createTextNode( mFixedRange.end().toTimeSpec( Qt::OffsetFromUTC ).toString( Qt::ISODate ) );
320 startElement.appendChild( startText );
321 endElement.appendChild( endText );
322 rangeElement.appendChild( startElement );
323 rangeElement.appendChild( endElement );
324
325 temporalElement.appendChild( rangeElement );
326
327 element.appendChild( temporalElement );
328
329 return element;
330}
331
333{
334 if ( const QgsVectorDataProviderTemporalCapabilities *vectorCaps = dynamic_cast< const QgsVectorDataProviderTemporalCapabilities *>( capabilities ) )
335 {
336 setIsActive( vectorCaps->hasTemporalCapabilities() );
337 setFixedTemporalRange( vectorCaps->availableTemporalRange() );
338 setStartField( vectorCaps->startField() );
339 setEndField( vectorCaps->endField() );
340 switch ( vectorCaps->mode() )
341 {
342 case Qgis::VectorDataProviderTemporalMode::HasFixedTemporalRange:
343 setMode( Qgis::VectorTemporalMode::FixedTemporalRange );
344 break;
345 case Qgis::VectorDataProviderTemporalMode::StoresFeatureDateTimeInstantInField:
346 setMode( Qgis::VectorTemporalMode::FeatureDateTimeInstantFromField );
347 break;
348 case Qgis::VectorDataProviderTemporalMode::StoresFeatureDateTimeStartAndEndInSeparateFields:
349 setMode( Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromFields );
350 break;
351 }
352 }
353}
354
356{
357 return mStartExpression;
358}
359
360void QgsVectorLayerTemporalProperties::setStartExpression( const QString &startExpression )
361{
362 mStartExpression = startExpression;
363}
364
366{
367 return mEndExpression;
368}
369
370void QgsVectorLayerTemporalProperties::setEndExpression( const QString &endExpression )
371{
372 mEndExpression = endExpression;
373}
374
376{
377 return mAccumulateFeatures;
378}
379
381{
382 mAccumulateFeatures = accumulateFeatures;
383}
384
386{
387 return mFixedDuration;
388}
389
391{
392 mFixedDuration = fixedDuration;
393}
394
396{
397 return mStartFieldName;
398}
399
400void QgsVectorLayerTemporalProperties::setStartField( const QString &startFieldName )
401{
402 mStartFieldName = startFieldName;
403}
404
406{
407 return mEndFieldName;
408}
409
411{
412 mEndFieldName = field;
413}
414
416{
417 return mDurationFieldName;
418}
419
421{
422 mDurationFieldName = field;
423}
424
426{
427 return mDurationUnit;
428}
429
431{
432 mDurationUnit = units;
433}
434
435QString dateTimeExpressionLiteral( const QDateTime &datetime )
436{
437 return QStringLiteral( "make_datetime(%1,%2,%3,%4,%5,%6)" ).arg( datetime.date().year() )
438 .arg( datetime.date().month() )
439 .arg( datetime.date().day() )
440 .arg( datetime.time().hour() )
441 .arg( datetime.time().minute() )
442 .arg( datetime.time().second() + datetime.time().msec() / 1000.0 );
443}
444
445QString QgsVectorLayerTemporalProperties::createFilterString( const QgsVectorLayerTemporalContext &context, const QgsDateTimeRange &filterRange ) const
446{
447 if ( !isActive() )
448 return QString();
449
450 auto dateTimefieldCast = [ &context ]( const QString & fieldName ) -> QString
451 {
452 if ( context.layer()
453 && context.layer()->fields().lookupField( fieldName ) >= 0
454 && context.layer()->fields().at( context.layer()->fields().lookupField( fieldName ) ).type() != QVariant::DateTime )
455 {
456 return QStringLiteral( "to_datetime( %1 )" ) .arg( QgsExpression::quotedColumnRef( fieldName ) );
457 }
458 return QgsExpression::quotedColumnRef( fieldName );
459 };
460
461 switch ( mMode )
462 {
463 case Qgis::VectorTemporalMode::FixedTemporalRange:
464 case Qgis::VectorTemporalMode::RedrawLayerOnly:
465 return QString();
466
467 case Qgis::VectorTemporalMode::FeatureDateTimeInstantFromField:
468 {
469 if ( mAccumulateFeatures )
470 {
471 return QStringLiteral( "(%1 %2 %3) OR %1 IS NULL" ).arg( dateTimefieldCast( mStartFieldName ),
472 filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
473 dateTimeExpressionLiteral( filterRange.end() ) );
474 }
475 else if ( qgsDoubleNear( mFixedDuration, 0.0 ) )
476 {
477 return QStringLiteral( "(%1 %2 %3 AND %1 %4 %5) OR %1 IS NULL" ).arg( dateTimefieldCast( mStartFieldName ),
478 filterRange.includeBeginning() ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
479 dateTimeExpressionLiteral( filterRange.begin() ),
480 filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
481 dateTimeExpressionLiteral( filterRange.end() ) );
482 }
483 else
484 {
485 // Working with features with events with a duration, so taking this duration into account (+ QgsInterval( -mFixedDuration, mDurationUnit ) ))
486 return QStringLiteral( "(%1 %2 %3 AND %1 %4 %5) OR %1 IS NULL" ).arg( dateTimefieldCast( mStartFieldName ),
487 limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
488 dateTimeExpressionLiteral( filterRange.begin() + QgsInterval( -mFixedDuration, mDurationUnit ) ),
489 filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
490 dateTimeExpressionLiteral( filterRange.end() ) );
491 }
492 }
493
494 case Qgis::VectorTemporalMode::FeatureDateTimeStartAndDurationFromFields:
495 {
496 QString intervalExpression;
497 switch ( mDurationUnit )
498 {
500 intervalExpression = QStringLiteral( "make_interval(0,0,0,0,0,0,%1/1000)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
501 break;
502
504 intervalExpression = QStringLiteral( "make_interval(0,0,0,0,0,0,%1)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
505 break;
506
508 intervalExpression = QStringLiteral( "make_interval(0,0,0,0,0,%1,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
509 break;
510
512 intervalExpression = QStringLiteral( "make_interval(0,0,0,0,%1,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
513 break;
514
516 intervalExpression = QStringLiteral( "make_interval(0,0,0,%1,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
517 break;
518
520 intervalExpression = QStringLiteral( "make_interval(0,0,%1,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
521 break;
522
524 intervalExpression = QStringLiteral( "make_interval(0,%1,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
525 break;
526
528 intervalExpression = QStringLiteral( "make_interval(%1,0,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
529 break;
530
532 intervalExpression = QStringLiteral( "make_interval(10 * %1,0,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
533 break;
534
536 intervalExpression = QStringLiteral( "make_interval(100 * %1,0,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
537 break;
538
541 return QString();
542 }
543 return QStringLiteral( "(%1 %2 %3 OR %1 IS NULL) AND ((%1 + %4 %5 %6) OR %7 IS NULL)" ).arg( dateTimefieldCast( mStartFieldName ),
544 filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
545 dateTimeExpressionLiteral( filterRange.end() ),
546 intervalExpression,
547 limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
548 dateTimeExpressionLiteral( filterRange.begin() ),
549 QgsExpression::quotedColumnRef( mDurationFieldName ) );
550 }
551
552 case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromFields:
553 {
554 if ( !mStartFieldName.isEmpty() && !mEndFieldName.isEmpty() )
555 {
556 return QStringLiteral( "(%1 %2 %3 OR %1 IS NULL) AND (%4 %5 %6 OR %4 IS NULL)" ).arg( dateTimefieldCast( mStartFieldName ),
557 filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
558 dateTimeExpressionLiteral( filterRange.end() ),
559 dateTimefieldCast( mEndFieldName ),
560 limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
561 dateTimeExpressionLiteral( filterRange.begin() ) );
562
563 }
564 else if ( !mStartFieldName.isEmpty() )
565 {
566 return QStringLiteral( "%1 %2 %3 OR %1 IS NULL" ).arg( dateTimefieldCast( mStartFieldName ),
567 filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
568 dateTimeExpressionLiteral( filterRange.end() ) );
569 }
570 else if ( !mEndFieldName.isEmpty() )
571 {
572 return QStringLiteral( "%1 %2 %3 OR %1 IS NULL" ).arg( dateTimefieldCast( mEndFieldName ),
573 limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
574 dateTimeExpressionLiteral( filterRange.begin() ) );
575 }
576 break;
577 }
578
579 case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromExpressions:
580 {
581 if ( !mStartExpression.isEmpty() && !mEndExpression.isEmpty() )
582 {
583 return QStringLiteral( "((%1) %2 %3) AND ((%4) %5 %6)" ).arg( mStartExpression,
584 filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
585 dateTimeExpressionLiteral( filterRange.end() ),
586 mEndExpression,
587 limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
588 dateTimeExpressionLiteral( filterRange.begin() ) );
589 }
590 else if ( !mStartExpression.isEmpty() )
591 {
592 return QStringLiteral( "(%1) %2 %3" ).arg( mStartExpression,
593 filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
594 dateTimeExpressionLiteral( filterRange.end() ) );
595 }
596 else if ( !mEndExpression.isEmpty() )
597 {
598 return QStringLiteral( "(%1) %2 %3" ).arg( mEndExpression,
599 limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
600 dateTimeExpressionLiteral( filterRange.begin() ) );
601 }
602 break;
603 }
604 }
605
606 return QString();
607}
608
610{
611
612 // Check the fields and keep the first one that matches.
613 // We assume that the user has organized the data with the
614 // more "interesting" field names first.
615 // This candidates list is a prioritized list of candidates ranked by "interestingness"!
616 // See discussion at https://github.com/qgis/QGIS/pull/30245 - this list must NOT be translated,
617 // but adding hardcoded localized variants of the strings is encouraged.
618 static const QStringList sStartCandidates{ QStringLiteral( "start" ),
619 QStringLiteral( "begin" ),
620 QStringLiteral( "from" )};
621
622 static const QStringList sEndCandidates{ QStringLiteral( "end" ),
623 QStringLiteral( "last" ),
624 QStringLiteral( "to" )};
625
626 static const QStringList sSingleFieldCandidates{ QStringLiteral( "event" ) };
627
628
629 bool foundStart = false;
630 bool foundEnd = false;
631
632 for ( const QgsField &field : fields )
633 {
634 if ( field.type() != QVariant::Date && field.type() != QVariant::DateTime )
635 continue;
636
637 if ( !foundStart )
638 {
639 for ( const QString &candidate : sStartCandidates )
640 {
641 const QString fldName = field.name();
642 if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
643 {
644 mStartFieldName = fldName;
645 foundStart = true;
646 }
647 }
648 }
649
650 if ( !foundEnd )
651 {
652 for ( const QString &candidate : sEndCandidates )
653 {
654 const QString fldName = field.name();
655 if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
656 {
657 mEndFieldName = fldName;
658 foundEnd = true;
659 }
660 }
661 }
662
663 if ( foundStart && foundEnd )
664 break;
665 }
666
667 if ( !foundStart )
668 {
669 // loop again, looking for likely "single field" candidates
670 for ( const QgsField &field : fields )
671 {
672 if ( field.type() != QVariant::Date && field.type() != QVariant::DateTime )
673 continue;
674
675 for ( const QString &candidate : sSingleFieldCandidates )
676 {
677 const QString fldName = field.name();
678 if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
679 {
680 mStartFieldName = fldName;
681 foundStart = true;
682 }
683 }
684
685 if ( foundStart )
686 break;
687 }
688 }
689
690 if ( foundStart && foundEnd )
691 mMode = Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromFields;
692 else if ( foundStart )
693 mMode = Qgis::VectorTemporalMode::FeatureDateTimeInstantFromField;
694
695 // note -- NEVER auto enable temporal properties here! It's just a helper designed
696 // to shortcut the initial field selection
697}
698
700{
701 return mLayer;
702}
703
705{
706 mLayer = layer;
707}
VectorTemporalMode
Vector layer temporal feature modes.
Definition: qgis.h:1232
VectorTemporalLimitMode
Mode for the handling of the limits of the filtering timeframe for vector features.
Definition: qgis.h:1248
@ IncludeBeginIncludeEnd
Mode to include both limits of the filtering timeframe.
Base class for handling properties relating to a data provider's temporal capabilities.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:338
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
QVariant::Type type
Definition: qgsfield.h:58
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
A representation of the interval between two datetime values.
Definition: qgsinterval.h:42
Base class for storage of map layer temporal properties.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
The class is used as a container of context for various read/write operations on other objects.
bool isActive() const
Returns true if the temporal property is active.
void setIsActive(bool active)
Sets whether the temporal property is active.
@ FlagDontInvalidateCachedRendersWhenRangeChanges
Any cached rendering will not be invalidated when temporal range context is modified.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
TemporalUnit
Temporal units.
Definition: qgsunittypes.h:150
@ TemporalMonths
Months.
Definition: qgsunittypes.h:157
@ TemporalUnknownUnit
Unknown time unit.
Definition: qgsunittypes.h:162
@ TemporalWeeks
Weeks.
Definition: qgsunittypes.h:156
@ TemporalMilliseconds
Milliseconds.
Definition: qgsunittypes.h:151
@ TemporalIrregularStep
Special "irregular step" time unit, used for temporal data which uses irregular, non-real-world unit ...
Definition: qgsunittypes.h:161
@ TemporalDays
Days.
Definition: qgsunittypes.h:155
@ TemporalDecades
Decades.
Definition: qgsunittypes.h:159
@ TemporalCenturies
Centuries.
Definition: qgsunittypes.h:160
@ TemporalSeconds
Seconds.
Definition: qgsunittypes.h:152
@ TemporalMinutes
Minutes.
Definition: qgsunittypes.h:153
@ TemporalYears
Years.
Definition: qgsunittypes.h:158
@ TemporalHours
Hours.
Definition: qgsunittypes.h:154
static Q_INVOKABLE QgsUnitTypes::TemporalUnit decodeTemporalUnit(const QString &string, bool *ok=nullptr)
Decodes a temporal unit from a string.
Implementation of data provider temporal properties for QgsVectorDataProviders.
Encapsulates the context in which a QgsVectorLayer's temporal capabilities will be applied.
QgsVectorLayer * layer() const
Returns the associated layer.
void setLayer(QgsVectorLayer *layer)
Sets the associated layer.
void guessDefaultsFromFields(const QgsFields &fields)
Attempts to setup the temporal properties by scanning a set of fields and looking for standard naming...
QString endExpression() const
Returns the expression for the end time for the feature's time spans.
void setDurationField(const QString &field)
Sets the name of the duration field, which contains the duration of the event.
void setMode(Qgis::VectorTemporalMode mode)
Sets the temporal properties mode.
QgsVectorLayerTemporalProperties(QObject *parent=nullptr, bool enabled=false)
Constructor for QgsVectorLayerTemporalProperties, with the specified parent object.
void setStartExpression(const QString &expression)
Sets the expression to use for the start time for the feature's time spans.
bool isVisibleInTemporalRange(const QgsDateTimeRange &range) const override
Returns true if the layer should be visible and rendered for the specified time range.
Qgis::VectorTemporalLimitMode limitMode() const
Returns the temporal limit mode (to include or exclude begin/end limits).
void setLimitMode(Qgis::VectorTemporalLimitMode mode)
Sets the temporal limit mode (to include or exclude begin/end limits).
const QgsDateTimeRange & fixedTemporalRange() const
Returns the fixed temporal range for the layer.
double fixedDuration() const
Returns the fixed duration length, which contains the duration of the event.
bool accumulateFeatures() const
Returns true if features will be accumulated over time (i.e.
QgsTemporalProperty::Flags flags() const override
Returns flags associated to the temporal property.
void setFixedTemporalRange(const QgsDateTimeRange &range)
Sets a temporal range to apply to the whole layer.
QgsUnitTypes::TemporalUnit durationUnits() const
Returns the units of the event's duration.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context) override
Reads temporal properties from a DOM element previously written by writeXml().
void setEndExpression(const QString &endExpression)
Sets the expression to use for the end time for the feature's time spans.
QString durationField() const
Returns the name of the duration field, which contains the duration of the event.
QString endField() const
Returns the name of the end datetime field, which contains the end time for the feature's time spans.
QString createFilterString(const QgsVectorLayerTemporalContext &context, const QgsDateTimeRange &range) const
Creates a QGIS expression filter string for filtering features within the specified context to those ...
void setDurationUnits(QgsUnitTypes::TemporalUnit units)
Sets the units of the event's duration.
QDomElement writeXml(QDomElement &element, QDomDocument &doc, const QgsReadWriteContext &context) override
Writes the properties to a DOM element, to be used later with readXml().
void setAccumulateFeatures(bool accumulate)
Sets whether features will be accumulated over time (i.e.
void setFixedDuration(double duration)
Sets the fixed event duration, which contains the duration of the event.
void setEndField(const QString &field)
Sets the name of the end datetime field, which contains the end time for the feature's time spans.
QgsDateTimeRange calculateTemporalExtent(QgsMapLayer *layer) const override
Attempts to calculate the overall temporal extent for the specified layer, using the settings defined...
void setDefaultsFromDataProviderTemporalCapabilities(const QgsDataProviderTemporalCapabilities *capabilities) override
Sets the layers temporal settings to appropriate defaults based on a provider's temporal capabilities...
Qgis::VectorTemporalMode mode() const
Returns the temporal properties mode.
QString startField() const
Returns the name of the start datetime field, which contains the start time for the feature's time sp...
void setStartField(const QString &field)
Sets the name of the start datetime field, which contains the start time for the feature's time spans...
QString startExpression() const
Returns the expression for the start time for the feature's time spans.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QVariant minimumValue(int index) const FINAL
Returns the minimum value for an attribute column or an invalid variant in case of error.
void minimumAndMaximumValue(int index, QVariant &minimum, QVariant &maximum) const
Calculates both the minimum and maximum value for an attribute column.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:2466
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
QList< int > QgsAttributeList
Definition: qgsfield.h:26
const QgsField & field
Definition: qgsfield.h:463
QString dateTimeExpressionLiteral(const QDateTime &datetime)