QGIS API Documentation  3.27.0-Master (e113457133)
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 
30 bool 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 
183  QgsFeatureRequest req;
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 
244 QgsTemporalProperty::Flags QgsVectorLayerTemporalProperties::flags() const
245 {
246  return mode() == Qgis::VectorTemporalMode::FixedTemporalRange ? QgsTemporalProperty::FlagDontInvalidateCachedRendersWhenRangeChanges : QgsTemporalProperty::Flags();
247 }
248 
249 void QgsVectorLayerTemporalProperties::setFixedTemporalRange( const QgsDateTimeRange &range )
250 {
251  mFixedRange = range;
252 }
253 
255 {
256  return mFixedRange;
257 }
258 
259 bool 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 
293 QDomElement 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 
360 void QgsVectorLayerTemporalProperties::setStartExpression( const QString &startExpression )
361 {
362  mStartExpression = startExpression;
363 }
364 
366 {
367  return mEndExpression;
368 }
369 
370 void 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 
400 void 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 
435 QString 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 
445 QString 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( QgsExpression::quotedColumnRef( 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  QgsExpression::quotedColumnRef( 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:1167
VectorTemporalLimitMode
Mode for the handling of the limits of the filtering timeframe for vector features.
Definition: qgis.h:1183
@ 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:320
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:2199
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2260
QList< int > QgsAttributeList
Definition: qgsfield.h:26
const QgsField & field
Definition: qgsfield.h:463
QString dateTimeExpressionLiteral(const QDateTime &datetime)