37     case Qgis::VectorTemporalMode::FixedTemporalRange:
 
   38       return range.isInfinite() || mFixedRange.isInfinite() || mFixedRange.overlaps( range );
 
   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:
 
   52   QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
 
   54     return QgsDateTimeRange();
 
   58     case Qgis::VectorTemporalMode::FixedTemporalRange:
 
   61     case Qgis::VectorTemporalMode::FeatureDateTimeInstantFromField:
 
   64       if ( fieldIndex >= 0 )
 
   70         const QDateTime min = minVal.toDateTime();
 
   71         const QDateTime maxStartTime = maxVal.toDateTime();
 
   73         return QgsDateTimeRange( min, maxStartTime + eventDuration );
 
   78     case Qgis::VectorTemporalMode::FeatureDateTimeStartAndDurationFromFields:
 
   81       const int durationFieldIndex = vectorLayer->
fields().
lookupField( mDurationFieldName );
 
   82       if ( fieldIndex >= 0 && durationFieldIndex >= 0 )
 
   84         const QDateTime minTime = vectorLayer->
minimumValue( fieldIndex ).toDateTime();
 
   92           const QDateTime start = f.
attribute( fieldIndex ).toDateTime();
 
   93           if ( start.isValid() )
 
   95             const QVariant durationValue = f.
attribute( durationFieldIndex );
 
   96             if ( durationValue.isValid() )
 
   98               const double duration = durationValue.toDouble();
 
   99               const QDateTime end = start.addMSecs( 
QgsInterval( duration, mDurationUnit ).seconds() * 1000.0 );
 
  101                 maxTime = maxTime.isValid() ? std::max( maxTime, end ) : end;
 
  105         return QgsDateTimeRange( minTime, maxTime );
 
  110     case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromFields:
 
  112       const int startFieldIndex = vectorLayer->
fields().
lookupField( mStartFieldName );
 
  114       if ( startFieldIndex >= 0 && endFieldIndex >= 0 )
 
  116         QVariant startMinVal;
 
  117         QVariant startMaxVal;
 
  123         return QgsDateTimeRange( std::min( startMinVal.toDateTime(),
 
  124                                            endMinVal.toDateTime() ),
 
  125                                  std::max( startMaxVal.toDateTime(),
 
  126                                            endMaxVal.toDateTime() ) );
 
  128       else if ( startFieldIndex >= 0 )
 
  130         QVariant startMinVal;
 
  131         QVariant startMaxVal;
 
  133         return QgsDateTimeRange( startMinVal.toDateTime(),
 
  134                                  startMaxVal.toDateTime() );
 
  136       else if ( endFieldIndex >= 0 )
 
  141         return QgsDateTimeRange( endMinVal.toDateTime(),
 
  142                                  endMaxVal.toDateTime() );
 
  147     case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromExpressions:
 
  149       const bool hasStartExpression = !mStartExpression.isEmpty();
 
  150       const bool hasEndExpression = !mEndExpression.isEmpty();
 
  151       if ( !hasStartExpression && !hasEndExpression )
 
  152         return QgsDateTimeRange();
 
  162       if ( hasStartExpression )
 
  169       if ( hasEndExpression )
 
  175       QSet< QString > fields;
 
  176       if ( hasStartExpression )
 
  178       if ( hasEndExpression )
 
  194         const QDateTime start = hasStartExpression ? 
startExpression.evaluate( &context ).toDateTime() : QDateTime();
 
  195         const QDateTime end = hasEndExpression ? 
endExpression.evaluate( &context ).toDateTime() : QDateTime();
 
  197         if ( start.isValid() )
 
  199           minTime = minTime.isValid() ? std::min( minTime, start ) : start;
 
  200           if ( !hasEndExpression )
 
  201             maxTime = maxTime.isValid() ? std::max( maxTime, start ) : start;
 
  205           maxTime = maxTime.isValid() ? std::max( maxTime, end ) : end;
 
  206           if ( !hasStartExpression )
 
  207             minTime = minTime.isValid() ? std::min( minTime, end ) : end;
 
  210       return QgsDateTimeRange( minTime, maxTime );
 
  213     case Qgis::VectorTemporalMode::RedrawLayerOnly:
 
  217   return QgsDateTimeRange();
 
  263   const QDomElement temporalNode = element.firstChildElement( QStringLiteral( 
"temporal" ) );
 
  265   setIsActive( temporalNode.attribute( QStringLiteral( 
"enabled" ), QStringLiteral( 
"0" ) ).toInt() );
 
  267   mMode = 
static_cast< Qgis::VectorTemporalMode >( temporalNode.attribute( QStringLiteral( 
"mode" ), QStringLiteral( 
"0" ) ). toInt() );
 
  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" ) );
 
  276   mFixedDuration = temporalNode.attribute( QStringLiteral( 
"fixedDuration" ) ).toDouble();
 
  277   mAccumulateFeatures = temporalNode.attribute( QStringLiteral( 
"accumulate" ), QStringLiteral( 
"0" ) ).toInt();
 
  279   const QDomNode rangeElement = temporalNode.namedItem( QStringLiteral( 
"fixedRange" ) );
 
  281   const QDomNode begin = rangeElement.namedItem( QStringLiteral( 
"start" ) );
 
  282   const QDomNode end = rangeElement.namedItem( QStringLiteral( 
"end" ) );
 
  284   const QDateTime beginDate = QDateTime::fromString( begin.toElement().text(), Qt::ISODate );
 
  285   const QDateTime endDate = QDateTime::fromString( end.toElement().text(), Qt::ISODate );
 
  287   const QgsDateTimeRange range = QgsDateTimeRange( beginDate, endDate );
 
  296   if ( element.isNull() )
 
  297     return QDomElement();
 
  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 ) ) );
 
  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 );
 
  310   temporalElement.setAttribute( QStringLiteral( 
"fixedDuration" ), 
qgsDoubleToString( mFixedDuration ) );
 
  311   temporalElement.setAttribute( QStringLiteral( 
"accumulate" ), mAccumulateFeatures ? QStringLiteral( 
"1" ) : QStringLiteral( 
"0" ) );
 
  313   QDomElement rangeElement = document.createElement( QStringLiteral( 
"fixedRange" ) );
 
  315   QDomElement startElement = document.createElement( QStringLiteral( 
"start" ) );
 
  316   QDomElement endElement = document.createElement( QStringLiteral( 
"end" ) );
 
  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 );
 
  325   temporalElement.appendChild( rangeElement );
 
  327   element.appendChild( temporalElement );
 
  336     setIsActive( vectorCaps->hasTemporalCapabilities() );
 
  340     switch ( vectorCaps->mode() )
 
  342       case Qgis::VectorDataProviderTemporalMode::HasFixedTemporalRange:
 
  343         setMode( Qgis::VectorTemporalMode::FixedTemporalRange );
 
  345       case Qgis::VectorDataProviderTemporalMode::StoresFeatureDateTimeInstantInField:
 
  346         setMode( Qgis::VectorTemporalMode::FeatureDateTimeInstantFromField );
 
  348       case Qgis::VectorDataProviderTemporalMode::StoresFeatureDateTimeStartAndEndInSeparateFields:
 
  349         setMode( Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromFields );
 
  357   return mStartExpression;
 
  367   return mEndExpression;
 
  377   return mAccumulateFeatures;
 
  387   return mFixedDuration;
 
  397   return mStartFieldName;
 
  402   mStartFieldName = startFieldName;
 
  407   return mEndFieldName;
 
  412   mEndFieldName = 
field;
 
  417   return mDurationFieldName;
 
  422   mDurationFieldName = 
field;
 
  427   return mDurationUnit;
 
  432   mDurationUnit = units;
 
  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 );
 
  450   auto dateTimefieldCast = [ &context ]( 
const QString & fieldName ) -> QString
 
  463     case Qgis::VectorTemporalMode::FixedTemporalRange:
 
  464     case Qgis::VectorTemporalMode::RedrawLayerOnly:
 
  467     case Qgis::VectorTemporalMode::FeatureDateTimeInstantFromField:
 
  469       if ( mAccumulateFeatures )
 
  471         return QStringLiteral( 
"(%1 %2 %3) OR %1 IS NULL" ).arg( dateTimefieldCast( mStartFieldName ),
 
  472                filterRange.includeEnd() ? QStringLiteral( 
"<=" ) : QStringLiteral( 
"<" ),
 
  477         return QStringLiteral( 
"(%1 %2 %3 AND %1 %4 %5) OR %1 IS NULL" ).arg( dateTimefieldCast( mStartFieldName ),
 
  478                filterRange.includeBeginning() ? QStringLiteral( 
">=" ) : QStringLiteral( 
">" ),
 
  480                filterRange.includeEnd() ? QStringLiteral( 
"<=" ) : QStringLiteral( 
"<" ),
 
  486         return QStringLiteral( 
"(%1 %2 %3 AND %1 %4 %5) OR %1 IS NULL" ).arg( dateTimefieldCast( mStartFieldName ),
 
  489                filterRange.includeEnd() ? QStringLiteral( 
"<=" ) : QStringLiteral( 
"<" ),
 
  494     case Qgis::VectorTemporalMode::FeatureDateTimeStartAndDurationFromFields:
 
  496       QString intervalExpression;
 
  497       switch ( mDurationUnit )
 
  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( 
"<" ),
 
  552     case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromFields:
 
  554       if ( !mStartFieldName.isEmpty() && !mEndFieldName.isEmpty() )
 
  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( 
"<" ),
 
  559                dateTimefieldCast( mEndFieldName ),
 
  564       else if ( !mStartFieldName.isEmpty() )
 
  566         return QStringLiteral( 
"%1 %2 %3 OR %1 IS NULL" ).arg( dateTimefieldCast( mStartFieldName ),
 
  567                filterRange.includeEnd() ? QStringLiteral( 
"<=" ) : QStringLiteral( 
"<" ),
 
  570       else if ( !mEndFieldName.isEmpty() )
 
  572         return QStringLiteral( 
"%1 %2 %3 OR %1 IS NULL" ).arg( dateTimefieldCast( mEndFieldName ),
 
  579     case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromExpressions:
 
  581       if ( !mStartExpression.isEmpty() && !mEndExpression.isEmpty() )
 
  583         return QStringLiteral( 
"((%1) %2 %3) AND ((%4) %5 %6)" ).arg( mStartExpression,
 
  584                filterRange.includeEnd() ? QStringLiteral( 
"<=" ) : QStringLiteral( 
"<" ),
 
  590       else if ( !mStartExpression.isEmpty() )
 
  592         return QStringLiteral( 
"(%1) %2 %3" ).arg( mStartExpression,
 
  593                filterRange.includeEnd() ? QStringLiteral( 
"<=" ) : QStringLiteral( 
"<" ),
 
  596       else if ( !mEndExpression.isEmpty() )
 
  598         return QStringLiteral( 
"(%1) %2 %3" ).arg( mEndExpression,
 
  618   static const QStringList sStartCandidates{ QStringLiteral( 
"start" ),
 
  619       QStringLiteral( 
"begin" ),
 
  620       QStringLiteral( 
"from" )};
 
  622   static const QStringList sEndCandidates{ QStringLiteral( 
"end" ),
 
  623       QStringLiteral( 
"last" ),
 
  624       QStringLiteral( 
"to" )};
 
  626   static const QStringList sSingleFieldCandidates{ QStringLiteral( 
"event" ) };
 
  629   bool foundStart = 
false;
 
  630   bool foundEnd = 
false;
 
  639       for ( 
const QString &candidate : sStartCandidates )
 
  642         if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
 
  644           mStartFieldName = fldName;
 
  652       for ( 
const QString &candidate : sEndCandidates )
 
  655         if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
 
  657           mEndFieldName = fldName;
 
  663     if ( foundStart && foundEnd )
 
  675       for ( 
const QString &candidate : sSingleFieldCandidates )
 
  678         if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
 
  680           mStartFieldName = fldName;
 
  690   if ( foundStart && foundEnd )
 
  691     mMode = Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromFields;
 
  692   else if ( foundStart )
 
  693     mMode = Qgis::VectorTemporalMode::FeatureDateTimeInstantFromField;