QGIS API Documentation  3.21.0-Master (5b68dc587e)
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( QgsVectorLayerTemporalContext, const QgsDateTimeRange &filterRange ) const
446 {
447  if ( !isActive() )
448  return QString();
449 
450  switch ( mMode )
451  {
452  case Qgis::VectorTemporalMode::FixedTemporalRange:
453  case Qgis::VectorTemporalMode::RedrawLayerOnly:
454  return QString();
455 
456  case Qgis::VectorTemporalMode::FeatureDateTimeInstantFromField:
457  {
458  if ( mAccumulateFeatures )
459  {
460  return QStringLiteral( "(%1 %2 %3) OR %1 IS NULL" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
461  filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
462  dateTimeExpressionLiteral( filterRange.end() ) );
463  }
464  else if ( qgsDoubleNear( mFixedDuration, 0.0 ) )
465  {
466  return QStringLiteral( "(%1 %2 %3 AND %1 %4 %5) OR %1 IS NULL" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
467  filterRange.includeBeginning() ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
468  dateTimeExpressionLiteral( filterRange.begin() ),
469  filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
470  dateTimeExpressionLiteral( filterRange.end() ) );
471  }
472  else
473  {
474  // Working with features with events with a duration, so taking this duration into account (+ QgsInterval( -mFixedDuration, mDurationUnit ) ))
475  return QStringLiteral( "(%1 %2 %3 AND %1 %4 %5) OR %1 IS NULL" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
476  limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
477  dateTimeExpressionLiteral( filterRange.begin() + QgsInterval( -mFixedDuration, mDurationUnit ) ),
478  filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
479  dateTimeExpressionLiteral( filterRange.end() ) );
480  }
481  }
482 
483  case Qgis::VectorTemporalMode::FeatureDateTimeStartAndDurationFromFields:
484  {
485  QString intervalExpression;
486  switch ( mDurationUnit )
487  {
489  intervalExpression = QStringLiteral( "make_interval(0,0,0,0,0,0,%1/1000)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
490  break;
491 
493  intervalExpression = QStringLiteral( "make_interval(0,0,0,0,0,0,%1)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
494  break;
495 
497  intervalExpression = QStringLiteral( "make_interval(0,0,0,0,0,%1,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
498  break;
499 
501  intervalExpression = QStringLiteral( "make_interval(0,0,0,0,%1,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
502  break;
503 
505  intervalExpression = QStringLiteral( "make_interval(0,0,0,%1,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
506  break;
507 
509  intervalExpression = QStringLiteral( "make_interval(0,0,%1,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
510  break;
511 
513  intervalExpression = QStringLiteral( "make_interval(0,%1,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
514  break;
515 
517  intervalExpression = QStringLiteral( "make_interval(%1,0,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
518  break;
519 
521  intervalExpression = QStringLiteral( "make_interval(10 * %1,0,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
522  break;
523 
525  intervalExpression = QStringLiteral( "make_interval(100 * %1,0,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
526  break;
527 
530  return QString();
531  }
532  return QStringLiteral( "(%1 %2 %3 OR %1 IS NULL) AND ((%1 + %4 %5 %6) OR %7 IS NULL)" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
533  filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
534  dateTimeExpressionLiteral( filterRange.end() ),
535  intervalExpression,
536  limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
537  dateTimeExpressionLiteral( filterRange.begin() ),
538  QgsExpression::quotedColumnRef( mDurationFieldName ) );
539  }
540 
541  case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromFields:
542  {
543  if ( !mStartFieldName.isEmpty() && !mEndFieldName.isEmpty() )
544  {
545  return QStringLiteral( "(%1 %2 %3 OR %1 IS NULL) AND (%4 %5 %6 OR %4 IS NULL)" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
546  filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
547  dateTimeExpressionLiteral( filterRange.end() ),
548  QgsExpression::quotedColumnRef( mEndFieldName ),
549  limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
550  dateTimeExpressionLiteral( filterRange.begin() ) );
551 
552  }
553  else if ( !mStartFieldName.isEmpty() )
554  {
555  return QStringLiteral( "%1 %2 %3 OR %1 IS NULL" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
556  filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
557  dateTimeExpressionLiteral( filterRange.end() ) );
558  }
559  else if ( !mEndFieldName.isEmpty() )
560  {
561  return QStringLiteral( "%1 %2 %3 OR %1 IS NULL" ).arg( QgsExpression::quotedColumnRef( mEndFieldName ),
562  limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
563  dateTimeExpressionLiteral( filterRange.begin() ) );
564  }
565  break;
566  }
567 
568  case Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromExpressions:
569  {
570  if ( !mStartExpression.isEmpty() && !mEndExpression.isEmpty() )
571  {
572  return QStringLiteral( "((%1) %2 %3) AND ((%4) %5 %6)" ).arg( mStartExpression,
573  filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
574  dateTimeExpressionLiteral( filterRange.end() ),
575  mEndExpression,
576  limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
577  dateTimeExpressionLiteral( filterRange.begin() ) );
578  }
579  else if ( !mStartExpression.isEmpty() )
580  {
581  return QStringLiteral( "(%1) %2 %3" ).arg( mStartExpression,
582  filterRange.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
583  dateTimeExpressionLiteral( filterRange.end() ) );
584  }
585  else if ( !mEndExpression.isEmpty() )
586  {
587  return QStringLiteral( "(%1) %2 %3" ).arg( mEndExpression,
588  limitMode() == Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
589  dateTimeExpressionLiteral( filterRange.begin() ) );
590  }
591  break;
592  }
593  }
594 
595  return QString();
596 }
597 
599 {
600 
601  // Check the fields and keep the first one that matches.
602  // We assume that the user has organized the data with the
603  // more "interesting" field names first.
604  // This candidates list is a prioritized list of candidates ranked by "interestingness"!
605  // See discussion at https://github.com/qgis/QGIS/pull/30245 - this list must NOT be translated,
606  // but adding hardcoded localized variants of the strings is encouraged.
607  static const QStringList sStartCandidates{ QStringLiteral( "start" ),
608  QStringLiteral( "begin" ),
609  QStringLiteral( "from" )};
610 
611  static const QStringList sEndCandidates{ QStringLiteral( "end" ),
612  QStringLiteral( "last" ),
613  QStringLiteral( "to" )};
614 
615  static const QStringList sSingleFieldCandidates{ QStringLiteral( "event" ) };
616 
617 
618  bool foundStart = false;
619  bool foundEnd = false;
620 
621  for ( const QgsField &field : fields )
622  {
623  if ( field.type() != QVariant::Date && field.type() != QVariant::DateTime )
624  continue;
625 
626  if ( !foundStart )
627  {
628  for ( const QString &candidate : sStartCandidates )
629  {
630  const QString fldName = field.name();
631  if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
632  {
633  mStartFieldName = fldName;
634  foundStart = true;
635  }
636  }
637  }
638 
639  if ( !foundEnd )
640  {
641  for ( const QString &candidate : sEndCandidates )
642  {
643  const QString fldName = field.name();
644  if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
645  {
646  mEndFieldName = fldName;
647  foundEnd = true;
648  }
649  }
650  }
651 
652  if ( foundStart && foundEnd )
653  break;
654  }
655 
656  if ( !foundStart )
657  {
658  // loop again, looking for likely "single field" candidates
659  for ( const QgsField &field : fields )
660  {
661  if ( field.type() != QVariant::Date && field.type() != QVariant::DateTime )
662  continue;
663 
664  for ( const QString &candidate : sSingleFieldCandidates )
665  {
666  const QString fldName = field.name();
667  if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
668  {
669  mStartFieldName = fldName;
670  foundStart = true;
671  }
672  }
673 
674  if ( foundStart )
675  break;
676  }
677  }
678 
679  if ( foundStart && foundEnd )
680  mMode = Qgis::VectorTemporalMode::FeatureDateTimeStartAndEndFromFields;
681  else if ( foundStart )
682  mMode = Qgis::VectorTemporalMode::FeatureDateTimeInstantFromField;
683 
684  // note -- NEVER auto enable temporal properties here! It's just a helper designed
685  // to shortcut the initial field selection
686 }
687 
689 {
690  return mLayer;
691 }
692 
694 {
695  mLayer = layer;
696 }
VectorTemporalMode
Vector layer temporal feature modes.
Definition: qgis.h:827
VectorTemporalLimitMode
Mode for the handling of the limits of the filtering timeframe for vector features.
Definition: qgis.h:843
@ 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:302
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
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
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.
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 createFilterString(QgsVectorLayerTemporalContext context, const QgsDateTimeRange &range) const
Creates a QGIS expression filter string for filtering features within the specified context to those ...
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:1186
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1234
QList< int > QgsAttributeList
Definition: qgsfield.h:26
const QgsField & field
Definition: qgsfield.h:463
QString dateTimeExpressionLiteral(const QDateTime &datetime)