QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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( 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 }
QgsUnitTypes::TemporalDecades
@ TemporalDecades
Decades.
Definition: qgsunittypes.h:159
QgsFeatureRequest::NoGeometry
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition: qgsfeaturerequest.h:115
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:1052
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
QgsVectorLayerTemporalProperties::setStartField
void setStartField(const QString &field)
Sets the name of the start datetime field, which contains the start time for the feature's time spans...
Definition: qgsvectorlayertemporalproperties.cpp:400
qgsfields.h
qgsvectordataprovidertemporalcapabilities.h
qgsexpressioncontextutils.h
QgsExpressionContext::appendScopes
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
Definition: qgsexpressioncontext.cpp:499
QgsVectorLayerTemporalProperties::endField
QString endField() const
Returns the name of the end datetime field, which contains the end time for the feature's time spans.
Definition: qgsvectorlayertemporalproperties.cpp:405
QgsVectorLayerTemporalProperties::fixedTemporalRange
const QgsDateTimeRange & fixedTemporalRange() const
Returns the fixed temporal range for the layer.
Definition: qgsvectorlayertemporalproperties.cpp:254
QgsVectorLayerTemporalProperties::setEndField
void setEndField(const QString &field)
Sets the name of the end datetime field, which contains the end time for the feature's time spans.
Definition: qgsvectorlayertemporalproperties.cpp:410
QgsVectorLayerTemporalProperties::durationUnits
QgsUnitTypes::TemporalUnit durationUnits() const
Returns the units of the event's duration.
Definition: qgsvectorlayertemporalproperties.cpp:425
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:34
QgsVectorLayerTemporalProperties::setStartExpression
void setStartExpression(const QString &expression)
Sets the expression to use for the start time for the feature's time spans.
Definition: qgsvectorlayertemporalproperties.cpp:360
QgsVectorLayer::minimumAndMaximumValue
void minimumAndMaximumValue(int index, QVariant &minimum, QVariant &maximum) const
Calculates both the minimum and maximum value for an attribute column.
Definition: qgsvectorlayer.cpp:4384
QgsVectorLayerTemporalProperties::createFilterString
QString createFilterString(const QgsVectorLayerTemporalContext &context, const QgsDateTimeRange &range) const
Creates a QGIS expression filter string for filtering features within the specified context to those ...
Definition: qgsvectorlayertemporalproperties.cpp:445
qgsexpression.h
Qgis::VectorTemporalLimitMode::IncludeBeginIncludeEnd
@ IncludeBeginIncludeEnd
Mode to include both limits of the filtering timeframe.
QgsUnitTypes::TemporalYears
@ TemporalYears
Years.
Definition: qgsunittypes.h:158
QgsUnitTypes::TemporalDays
@ TemporalDays
Days.
Definition: qgsunittypes.h:155
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:44
QgsVectorLayerTemporalProperties::setLimitMode
void setLimitMode(Qgis::VectorTemporalLimitMode mode)
Sets the temporal limit mode (to include or exclude begin/end limits).
Definition: qgsvectorlayertemporalproperties.cpp:237
QgsUnitTypes::TemporalUnknownUnit
@ TemporalUnknownUnit
Unknown time unit.
Definition: qgsunittypes.h:162
Qgis::VectorTemporalMode
VectorTemporalMode
Vector layer temporal feature modes.
Definition: qgis.h:1166
QgsExpressionContextUtils::globalProjectLayerScopes
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Definition: qgsexpressioncontextutils.cpp:377
QgsVectorLayerTemporalProperties::guessDefaultsFromFields
void guessDefaultsFromFields(const QgsFields &fields)
Attempts to setup the temporal properties by scanning a set of fields and looking for standard naming...
Definition: qgsvectorlayertemporalproperties.cpp:609
QgsFeatureRequest::setSubsetOfAttributes
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Definition: qgsfeaturerequest.cpp:228
field
const QgsField & field
Definition: qgsfield.h:463
QgsUnitTypes::TemporalUnit
TemporalUnit
Temporal units.
Definition: qgsunittypes.h:149
QgsAttributeList
QList< int > QgsAttributeList
Definition: qgsfield.h:26
QgsVectorLayerTemporalProperties::setDurationField
void setDurationField(const QString &field)
Sets the name of the duration field, which contains the duration of the event.
Definition: qgsvectorlayertemporalproperties.cpp:420
QgsField::name
QString name
Definition: qgsfield.h:60
QgsVectorLayerTemporalProperties::startExpression
QString startExpression() const
Returns the expression for the start time for the feature's time spans.
Definition: qgsvectorlayertemporalproperties.cpp:355
qgsDoubleToString
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:2204
QgsUnitTypes::TemporalSeconds
@ TemporalSeconds
Seconds.
Definition: qgsunittypes.h:152
QgsUnitTypes::TemporalIrregularStep
@ TemporalIrregularStep
Special "irregular step" time unit, used for temporal data which uses irregular, non-real-world unit ...
Definition: qgsunittypes.h:161
QgsUnitTypes::TemporalMonths
@ TemporalMonths
Months.
Definition: qgsunittypes.h:157
QgsVectorLayerTemporalProperties::durationField
QString durationField() const
Returns the name of the duration field, which contains the duration of the event.
Definition: qgsvectorlayertemporalproperties.cpp:415
QgsDataProviderTemporalCapabilities
Base class for handling properties relating to a data provider's temporal capabilities.
Definition: qgsdataprovidertemporalcapabilities.h:33
QgsUnitTypes::decodeTemporalUnit
static Q_INVOKABLE QgsUnitTypes::TemporalUnit decodeTemporalUnit(const QString &string, bool *ok=nullptr)
Decodes a temporal unit from a string.
Definition: qgsunittypes.cpp:1286
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3436
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:83
qgsvectorlayertemporalproperties.h
QgsVectorLayerTemporalProperties::calculateTemporalExtent
QgsDateTimeRange calculateTemporalExtent(QgsMapLayer *layer) const override
Attempts to calculate the overall temporal extent for the specified layer, using the settings defined...
Definition: qgsvectorlayertemporalproperties.cpp:50
QgsVectorLayerTemporalProperties::setAccumulateFeatures
void setAccumulateFeatures(bool accumulate)
Sets whether features will be accumulated over time (i.e.
Definition: qgsvectorlayertemporalproperties.cpp:380
QgsVectorLayerTemporalProperties::QgsVectorLayerTemporalProperties
QgsVectorLayerTemporalProperties(QObject *parent=nullptr, bool enabled=false)
Constructor for QgsVectorLayerTemporalProperties, with the specified parent object.
Definition: qgsvectorlayertemporalproperties.cpp:25
QgsVectorLayerTemporalProperties::readXml
bool readXml(const QDomElement &element, const QgsReadWriteContext &context) override
Reads temporal properties from a DOM element previously written by writeXml().
Definition: qgsvectorlayertemporalproperties.cpp:259
QgsUnitTypes::encodeUnit
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
Definition: qgsunittypes.cpp:122
QgsTemporalProperty::isActive
bool isActive() const
Returns true if the temporal property is active.
Definition: qgstemporalproperty.cpp:36
QgsVectorLayerTemporalProperties::setFixedDuration
void setFixedDuration(double duration)
Sets the fixed event duration, which contains the duration of the event.
Definition: qgsvectorlayertemporalproperties.cpp:390
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2265
QgsVectorLayerTemporalProperties::endExpression
QString endExpression() const
Returns the expression for the end time for the feature's time spans.
Definition: qgsvectorlayertemporalproperties.cpp:365
QgsVectorLayerTemporalContext::setLayer
void setLayer(QgsVectorLayer *layer)
Sets the associated layer.
Definition: qgsvectorlayertemporalproperties.cpp:704
QgsVectorLayerTemporalProperties::setDefaultsFromDataProviderTemporalCapabilities
void setDefaultsFromDataProviderTemporalCapabilities(const QgsDataProviderTemporalCapabilities *capabilities) override
Sets the layers temporal settings to appropriate defaults based on a provider's temporal capabilities...
Definition: qgsvectorlayertemporalproperties.cpp:332
QgsVectorLayerTemporalProperties::startField
QString startField() const
Returns the name of the start datetime field, which contains the start time for the feature's time sp...
Definition: qgsvectorlayertemporalproperties.cpp:395
QgsFeature::attribute
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:327
QgsMapLayerTemporalProperties
Base class for storage of map layer temporal properties.
Definition: qgsmaplayertemporalproperties.h:42
QgsVectorLayerTemporalContext
Encapsulates the context in which a QgsVectorLayer's temporal capabilities will be applied.
Definition: qgsvectorlayertemporalproperties.h:40
QgsVectorLayerTemporalProperties::fixedDuration
double fixedDuration() const
Returns the fixed duration length, which contains the duration of the event.
Definition: qgsvectorlayertemporalproperties.cpp:385
QgsTemporalProperty::setIsActive
void setIsActive(bool active)
Sets whether the temporal property is active.
Definition: qgstemporalproperty.cpp:27
QgsVectorLayerTemporalProperties::limitMode
Qgis::VectorTemporalLimitMode limitMode() const
Returns the temporal limit mode (to include or exclude begin/end limits).
Definition: qgsvectorlayertemporalproperties.cpp:232
QgsVectorLayer::minimumValue
QVariant minimumValue(int index) const FINAL
Returns the minimum value for an attribute column or an invalid variant in case of error.
Definition: qgsvectorlayer.cpp:4370
QgsUnitTypes::TemporalHours
@ TemporalHours
Hours.
Definition: qgsunittypes.h:154
qgsvectorlayer.h
QgsVectorLayerTemporalProperties::setDurationUnits
void setDurationUnits(QgsUnitTypes::TemporalUnit units)
Sets the units of the event's duration.
Definition: qgsvectorlayertemporalproperties.cpp:430
QgsVectorLayerTemporalProperties::isVisibleInTemporalRange
bool isVisibleInTemporalRange(const QgsDateTimeRange &range) const override
Returns true if the layer should be visible and rendered for the specified time range.
Definition: qgsvectorlayertemporalproperties.cpp:30
QgsUnitTypes::TemporalMinutes
@ TemporalMinutes
Minutes.
Definition: qgsunittypes.h:153
QgsUnitTypes::TemporalMilliseconds
@ TemporalMilliseconds
Milliseconds.
Definition: qgsunittypes.h:151
QgsUnitTypes::TemporalCenturies
@ TemporalCenturies
Centuries.
Definition: qgsunittypes.h:160
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:399
QgsVectorDataProviderTemporalCapabilities
Implementation of data provider temporal properties for QgsVectorDataProviders.
Definition: qgsvectordataprovidertemporalcapabilities.h:37
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsUnitTypes::TemporalWeeks
@ TemporalWeeks
Weeks.
Definition: qgsunittypes.h:156
QgsMapLayer
Base class for all map layer types. This is the base class for all map layer types (vector,...
Definition: qgsmaplayer.h:72
QgsInterval
A representation of the interval between two datetime values.
Definition: qgsinterval.h:41
QgsVectorLayerTemporalProperties::setEndExpression
void setEndExpression(const QString &endExpression)
Sets the expression to use for the end time for the feature's time spans.
Definition: qgsvectorlayertemporalproperties.cpp:370
QgsVectorLayerTemporalProperties::setMode
void setMode(Qgis::VectorTemporalMode mode)
Sets the temporal properties mode.
Definition: qgsvectorlayertemporalproperties.cpp:225
QgsExpression::quotedColumnRef
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
Definition: qgsexpression.cpp:68
QgsFeature
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:55
QgsVectorLayerTemporalProperties::mode
Qgis::VectorTemporalMode mode() const
Returns the temporal properties mode.
Definition: qgsvectorlayertemporalproperties.cpp:220
QgsVectorLayerTemporalProperties::setFixedTemporalRange
void setFixedTemporalRange(const QgsDateTimeRange &range)
Sets a temporal range to apply to the whole layer.
Definition: qgsvectorlayertemporalproperties.cpp:249
Qgis::VectorTemporalLimitMode
VectorTemporalLimitMode
Mode for the handling of the limits of the filtering timeframe for vector features.
Definition: qgis.h:1182
QgsVectorLayerTemporalProperties::flags
QgsTemporalProperty::Flags flags() const override
Returns flags associated to the temporal property.
Definition: qgsvectorlayertemporalproperties.cpp:244
QgsFields::lookupField
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
QgsVectorLayerTemporalProperties::accumulateFeatures
bool accumulateFeatures() const
Returns true if features will be accumulated over time (i.e.
Definition: qgsvectorlayertemporalproperties.cpp:375
dateTimeExpressionLiteral
QString dateTimeExpressionLiteral(const QDateTime &datetime)
Definition: qgsvectorlayertemporalproperties.cpp:435
QgsFields::at
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:289
QgsVectorLayerTemporalProperties::writeXml
QDomElement writeXml(QDomElement &element, QDomDocument &doc, const QgsReadWriteContext &context) override
Writes the properties to a DOM element, to be used later with readXml().
Definition: qgsvectorlayertemporalproperties.cpp:293
QgsVectorLayerTemporalContext::layer
QgsVectorLayer * layer() const
Returns the associated layer.
Definition: qgsvectorlayertemporalproperties.cpp:699
QgsFeatureRequest::setFlags
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
Definition: qgsfeaturerequest.cpp:222
QgsTemporalProperty::FlagDontInvalidateCachedRendersWhenRangeChanges
@ FlagDontInvalidateCachedRendersWhenRangeChanges
Any cached rendering will not be invalidated when temporal range context is modified.
Definition: qgstemporalproperty.h:46
QgsField::type
QVariant::Type type
Definition: qgsfield.h:58
QgsExpressionContext::setFeature
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Definition: qgsexpressioncontext.cpp:525
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50