Branch data Line data Source code
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 : :
18 : : #include "qgsvectorlayertemporalproperties.h"
19 : : #include "qgsvectordataprovidertemporalcapabilities.h"
20 : : #include "qgsexpression.h"
21 : : #include "qgsvectorlayer.h"
22 : : #include "qgsfields.h"
23 : : #include "qgsexpressioncontextutils.h"
24 : :
25 : 78 : QgsVectorLayerTemporalProperties::QgsVectorLayerTemporalProperties( QObject *parent, bool enabled )
26 : 78 : : QgsMapLayerTemporalProperties( parent, enabled )
27 : 156 : {
28 : 78 : }
29 : :
30 : 0 : bool QgsVectorLayerTemporalProperties::isVisibleInTemporalRange( const QgsDateTimeRange &range ) const
31 : : {
32 : 0 : if ( !isActive() )
33 : 0 : return true;
34 : :
35 : 0 : switch ( mMode )
36 : : {
37 : : case ModeFixedTemporalRange:
38 : 0 : return range.isInfinite() || mFixedRange.isInfinite() || mFixedRange.overlaps( range );
39 : :
40 : : case ModeFeatureDateTimeInstantFromField:
41 : : case ModeFeatureDateTimeStartAndEndFromFields:
42 : : case ModeRedrawLayerOnly:
43 : : case ModeFeatureDateTimeStartAndDurationFromFields:
44 : : case ModeFeatureDateTimeStartAndEndFromExpressions:
45 : 0 : return true;
46 : : }
47 : 0 : return true;
48 : 0 : }
49 : :
50 : 0 : QgsDateTimeRange QgsVectorLayerTemporalProperties::calculateTemporalExtent( QgsMapLayer *layer ) const
51 : : {
52 : 0 : QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
53 : 0 : if ( !layer )
54 : 0 : return QgsDateTimeRange();
55 : :
56 : 0 : switch ( mMode )
57 : : {
58 : : case QgsVectorLayerTemporalProperties::ModeFixedTemporalRange:
59 : 0 : return mFixedRange;
60 : :
61 : : case QgsVectorLayerTemporalProperties::ModeFeatureDateTimeInstantFromField:
62 : : {
63 : 0 : const int fieldIndex = vectorLayer->fields().lookupField( mStartFieldName );
64 : 0 : if ( fieldIndex >= 0 )
65 : : {
66 : 0 : QVariant minVal;
67 : 0 : QVariant maxVal;
68 : 0 : vectorLayer->minimumAndMaximumValue( fieldIndex, minVal, maxVal );
69 : :
70 : 0 : const QDateTime min = minVal.toDateTime();
71 : 0 : const QDateTime maxStartTime = maxVal.toDateTime();
72 : 0 : const QgsInterval eventDuration = QgsInterval( mFixedDuration, mDurationUnit );
73 : 0 : return QgsDateTimeRange( min, maxStartTime + eventDuration );
74 : 0 : }
75 : 0 : break;
76 : : }
77 : :
78 : : case QgsVectorLayerTemporalProperties::ModeFeatureDateTimeStartAndDurationFromFields:
79 : : {
80 : 0 : const int fieldIndex = vectorLayer->fields().lookupField( mStartFieldName );
81 : 0 : const int durationFieldIndex = vectorLayer->fields().lookupField( mDurationFieldName );
82 : 0 : if ( fieldIndex >= 0 && durationFieldIndex >= 0 )
83 : : {
84 : 0 : const QDateTime minTime = vectorLayer->minimumValue( fieldIndex ).toDateTime();
85 : : // no choice here but to loop through all features to calculate max time :(
86 : :
87 : 0 : QgsFeature f;
88 : 0 : QgsFeatureIterator it = vectorLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( QgsAttributeList() << durationFieldIndex << fieldIndex ) );
89 : 0 : QDateTime maxTime;
90 : 0 : while ( it.nextFeature( f ) )
91 : : {
92 : 0 : const QDateTime start = f.attribute( fieldIndex ).toDateTime();
93 : 0 : if ( start.isValid() )
94 : : {
95 : 0 : const QVariant durationValue = f.attribute( durationFieldIndex );
96 : 0 : if ( durationValue.isValid() )
97 : : {
98 : 0 : const double duration = durationValue.toDouble();
99 : 0 : const QDateTime end = start.addMSecs( QgsInterval( duration, mDurationUnit ).seconds() * 1000.0 );
100 : 0 : if ( end.isValid() )
101 : 0 : maxTime = maxTime.isValid() ? std::max( maxTime, end ) : end;
102 : 0 : }
103 : 0 : }
104 : 0 : }
105 : 0 : return QgsDateTimeRange( minTime, maxTime );
106 : 0 : }
107 : 0 : break;
108 : : }
109 : :
110 : : case QgsVectorLayerTemporalProperties::ModeFeatureDateTimeStartAndEndFromFields:
111 : : {
112 : 0 : const int startFieldIndex = vectorLayer->fields().lookupField( mStartFieldName );
113 : 0 : const int endFieldIndex = vectorLayer->fields().lookupField( mEndFieldName );
114 : 0 : if ( startFieldIndex >= 0 && endFieldIndex >= 0 )
115 : : {
116 : 0 : QVariant startMinVal;
117 : 0 : QVariant startMaxVal;
118 : 0 : vectorLayer->minimumAndMaximumValue( startFieldIndex, startMinVal, startMaxVal );
119 : 0 : QVariant endMinVal;
120 : 0 : QVariant endMaxVal;
121 : 0 : vectorLayer->minimumAndMaximumValue( endFieldIndex, endMinVal, endMaxVal );
122 : :
123 : 0 : return QgsDateTimeRange( std::min( startMinVal.toDateTime(),
124 : 0 : endMinVal.toDateTime() ),
125 : 0 : std::max( startMaxVal.toDateTime(),
126 : 0 : endMaxVal.toDateTime() ) );
127 : 0 : }
128 : 0 : else if ( startFieldIndex >= 0 )
129 : : {
130 : 0 : QVariant startMinVal;
131 : 0 : QVariant startMaxVal;
132 : 0 : vectorLayer->minimumAndMaximumValue( startFieldIndex, startMinVal, startMaxVal );
133 : 0 : return QgsDateTimeRange( startMinVal.toDateTime(),
134 : 0 : startMaxVal.toDateTime() );
135 : 0 : }
136 : 0 : else if ( endFieldIndex >= 0 )
137 : : {
138 : 0 : QVariant endMinVal;
139 : 0 : QVariant endMaxVal;
140 : 0 : vectorLayer->minimumAndMaximumValue( endFieldIndex, endMinVal, endMaxVal );
141 : 0 : return QgsDateTimeRange( endMinVal.toDateTime(),
142 : 0 : endMaxVal.toDateTime() );
143 : 0 : }
144 : 0 : break;
145 : : }
146 : :
147 : : case QgsVectorLayerTemporalProperties::ModeFeatureDateTimeStartAndEndFromExpressions:
148 : : {
149 : 0 : bool hasStartExpression = !mStartExpression.isEmpty();
150 : 0 : bool hasEndExpression = !mEndExpression.isEmpty();
151 : 0 : if ( !hasStartExpression && !hasEndExpression )
152 : 0 : return QgsDateTimeRange();
153 : :
154 : 0 : QDateTime minTime;
155 : 0 : QDateTime maxTime;
156 : :
157 : : // no choice here but to loop through all features
158 : 0 : QgsExpressionContext context;
159 : 0 : context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( vectorLayer ) );
160 : :
161 : 0 : QgsExpression startExpression;
162 : 0 : if ( hasStartExpression )
163 : : {
164 : 0 : startExpression.setExpression( mStartExpression );
165 : 0 : startExpression.prepare( &context );
166 : 0 : }
167 : :
168 : 0 : QgsExpression endExpression;
169 : 0 : if ( hasEndExpression )
170 : : {
171 : 0 : endExpression.setExpression( mEndExpression );
172 : 0 : endExpression.prepare( &context );
173 : 0 : }
174 : :
175 : 0 : QSet< QString > fields;
176 : 0 : if ( hasStartExpression )
177 : 0 : fields.unite( startExpression.referencedColumns() );
178 : 0 : if ( hasEndExpression )
179 : 0 : fields.unite( endExpression.referencedColumns() );
180 : :
181 : 0 : const bool needsGeom = startExpression.needsGeometry() || endExpression.needsGeometry();
182 : :
183 : 0 : QgsFeatureRequest req;
184 : 0 : if ( !needsGeom )
185 : 0 : req.setFlags( QgsFeatureRequest::NoGeometry );
186 : :
187 : 0 : req.setSubsetOfAttributes( fields, vectorLayer->fields() );
188 : :
189 : 0 : QgsFeature f;
190 : 0 : QgsFeatureIterator it = vectorLayer->getFeatures( req );
191 : 0 : while ( it.nextFeature( f ) )
192 : : {
193 : 0 : context.setFeature( f );
194 : 0 : const QDateTime start = hasStartExpression ? startExpression.evaluate( &context ).toDateTime() : QDateTime();
195 : 0 : const QDateTime end = hasEndExpression ? endExpression.evaluate( &context ).toDateTime() : QDateTime();
196 : :
197 : 0 : if ( start.isValid() )
198 : : {
199 : 0 : minTime = minTime.isValid() ? std::min( minTime, start ) : start;
200 : 0 : if ( !hasEndExpression )
201 : 0 : maxTime = maxTime.isValid() ? std::max( maxTime, start ) : start;
202 : 0 : }
203 : 0 : if ( end.isValid() )
204 : : {
205 : 0 : maxTime = maxTime.isValid() ? std::max( maxTime, end ) : end;
206 : 0 : if ( !hasStartExpression )
207 : 0 : minTime = minTime.isValid() ? std::min( minTime, end ) : end;
208 : 0 : }
209 : 0 : }
210 : 0 : return QgsDateTimeRange( minTime, maxTime );
211 : 0 : }
212 : :
213 : : case QgsVectorLayerTemporalProperties::ModeRedrawLayerOnly:
214 : 0 : break;
215 : : }
216 : :
217 : 0 : return QgsDateTimeRange();
218 : 0 : }
219 : :
220 : 0 : QgsVectorLayerTemporalProperties::TemporalMode QgsVectorLayerTemporalProperties::mode() const
221 : : {
222 : 0 : return mMode;
223 : : }
224 : :
225 : 78 : void QgsVectorLayerTemporalProperties::setMode( QgsVectorLayerTemporalProperties::TemporalMode mode )
226 : : {
227 : 78 : if ( mMode == mode )
228 : 78 : return;
229 : 0 : mMode = mode;
230 : 78 : }
231 : :
232 : 0 : QgsTemporalProperty::Flags QgsVectorLayerTemporalProperties::flags() const
233 : : {
234 : 0 : return mode() == ModeFixedTemporalRange ? QgsTemporalProperty::FlagDontInvalidateCachedRendersWhenRangeChanges : QgsTemporalProperty::Flags();
235 : : }
236 : :
237 : 78 : void QgsVectorLayerTemporalProperties::setFixedTemporalRange( const QgsDateTimeRange &range )
238 : : {
239 : 78 : mFixedRange = range;
240 : 78 : }
241 : :
242 : 0 : const QgsDateTimeRange &QgsVectorLayerTemporalProperties::fixedTemporalRange() const
243 : : {
244 : 0 : return mFixedRange;
245 : : }
246 : :
247 : 0 : bool QgsVectorLayerTemporalProperties::readXml( const QDomElement &element, const QgsReadWriteContext &context )
248 : : {
249 : 0 : Q_UNUSED( context )
250 : :
251 : 0 : QDomElement temporalNode = element.firstChildElement( QStringLiteral( "temporal" ) );
252 : :
253 : 0 : setIsActive( temporalNode.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt() );
254 : :
255 : 0 : mMode = static_cast< TemporalMode >( temporalNode.attribute( QStringLiteral( "mode" ), QStringLiteral( "0" ) ). toInt() );
256 : :
257 : 0 : mStartFieldName = temporalNode.attribute( QStringLiteral( "startField" ) );
258 : 0 : mEndFieldName = temporalNode.attribute( QStringLiteral( "endField" ) );
259 : 0 : mStartExpression = temporalNode.attribute( QStringLiteral( "startExpression" ) );
260 : 0 : mEndExpression = temporalNode.attribute( QStringLiteral( "endExpression" ) );
261 : 0 : mDurationFieldName = temporalNode.attribute( QStringLiteral( "durationField" ) );
262 : 0 : mDurationUnit = QgsUnitTypes::decodeTemporalUnit( temporalNode.attribute( QStringLiteral( "durationUnit" ), QgsUnitTypes::encodeUnit( QgsUnitTypes::TemporalMinutes ) ) );
263 : 0 : mFixedDuration = temporalNode.attribute( QStringLiteral( "fixedDuration" ) ).toDouble();
264 : 0 : mAccumulateFeatures = temporalNode.attribute( QStringLiteral( "accumulate" ), QStringLiteral( "0" ) ).toInt();
265 : :
266 : 0 : QDomNode rangeElement = temporalNode.namedItem( QStringLiteral( "fixedRange" ) );
267 : :
268 : 0 : QDomNode begin = rangeElement.namedItem( QStringLiteral( "start" ) );
269 : 0 : QDomNode end = rangeElement.namedItem( QStringLiteral( "end" ) );
270 : :
271 : 0 : QDateTime beginDate = QDateTime::fromString( begin.toElement().text(), Qt::ISODate );
272 : 0 : QDateTime endDate = QDateTime::fromString( end.toElement().text(), Qt::ISODate );
273 : :
274 : 0 : QgsDateTimeRange range = QgsDateTimeRange( beginDate, endDate );
275 : 0 : setFixedTemporalRange( range );
276 : :
277 : : return true;
278 : 0 : }
279 : :
280 : 0 : QDomElement QgsVectorLayerTemporalProperties::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context )
281 : : {
282 : 0 : Q_UNUSED( context )
283 : 0 : if ( element.isNull() )
284 : 0 : return QDomElement();
285 : :
286 : 0 : QDomElement temporalElement = document.createElement( QStringLiteral( "temporal" ) );
287 : 0 : temporalElement.setAttribute( QStringLiteral( "enabled" ), isActive() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
288 : 0 : temporalElement.setAttribute( QStringLiteral( "mode" ), QString::number( mMode ) );
289 : :
290 : 0 : temporalElement.setAttribute( QStringLiteral( "startField" ), mStartFieldName );
291 : 0 : temporalElement.setAttribute( QStringLiteral( "endField" ), mEndFieldName );
292 : 0 : temporalElement.setAttribute( QStringLiteral( "startExpression" ), mStartExpression );
293 : 0 : temporalElement.setAttribute( QStringLiteral( "endExpression" ), mEndExpression );
294 : 0 : temporalElement.setAttribute( QStringLiteral( "durationField" ), mDurationFieldName );
295 : 0 : temporalElement.setAttribute( QStringLiteral( "durationUnit" ), QgsUnitTypes::encodeUnit( mDurationUnit ) );
296 : 0 : temporalElement.setAttribute( QStringLiteral( "fixedDuration" ), qgsDoubleToString( mFixedDuration ) );
297 : 0 : temporalElement.setAttribute( QStringLiteral( "accumulate" ), mAccumulateFeatures ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
298 : :
299 : 0 : QDomElement rangeElement = document.createElement( QStringLiteral( "fixedRange" ) );
300 : :
301 : 0 : QDomElement startElement = document.createElement( QStringLiteral( "start" ) );
302 : 0 : QDomElement endElement = document.createElement( QStringLiteral( "end" ) );
303 : :
304 : 0 : QDomText startText = document.createTextNode( mFixedRange.begin().toTimeSpec( Qt::OffsetFromUTC ).toString( Qt::ISODate ) );
305 : 0 : QDomText endText = document.createTextNode( mFixedRange.end().toTimeSpec( Qt::OffsetFromUTC ).toString( Qt::ISODate ) );
306 : 0 : startElement.appendChild( startText );
307 : 0 : endElement.appendChild( endText );
308 : 0 : rangeElement.appendChild( startElement );
309 : 0 : rangeElement.appendChild( endElement );
310 : :
311 : 0 : temporalElement.appendChild( rangeElement );
312 : :
313 : 0 : element.appendChild( temporalElement );
314 : :
315 : 0 : return element;
316 : 0 : }
317 : :
318 : 78 : void QgsVectorLayerTemporalProperties::setDefaultsFromDataProviderTemporalCapabilities( const QgsDataProviderTemporalCapabilities *capabilities )
319 : : {
320 : 78 : if ( const QgsVectorDataProviderTemporalCapabilities *vectorCaps = dynamic_cast< const QgsVectorDataProviderTemporalCapabilities *>( capabilities ) )
321 : : {
322 : 78 : setIsActive( vectorCaps->hasTemporalCapabilities() );
323 : 78 : setFixedTemporalRange( vectorCaps->availableTemporalRange() );
324 : 78 : setStartField( vectorCaps->startField() );
325 : 78 : setEndField( vectorCaps->endField() );
326 : 78 : switch ( vectorCaps->mode() )
327 : : {
328 : : case QgsVectorDataProviderTemporalCapabilities::ProviderHasFixedTemporalRange:
329 : 78 : setMode( ModeFixedTemporalRange );
330 : 78 : break;
331 : : case QgsVectorDataProviderTemporalCapabilities::ProviderStoresFeatureDateTimeInstantInField:
332 : 0 : setMode( ModeFeatureDateTimeInstantFromField );
333 : 0 : break;
334 : : case QgsVectorDataProviderTemporalCapabilities::ProviderStoresFeatureDateTimeStartAndEndInSeparateFields:
335 : 0 : setMode( ModeFeatureDateTimeStartAndEndFromFields );
336 : 0 : break;
337 : : }
338 : 78 : }
339 : 78 : }
340 : :
341 : 0 : QString QgsVectorLayerTemporalProperties::startExpression() const
342 : : {
343 : 78 : return mStartExpression;
344 : : }
345 : :
346 : 0 : void QgsVectorLayerTemporalProperties::setStartExpression( const QString &startExpression )
347 : : {
348 : 0 : mStartExpression = startExpression;
349 : 0 : }
350 : :
351 : 78 : QString QgsVectorLayerTemporalProperties::endExpression() const
352 : : {
353 : 78 : return mEndExpression;
354 : : }
355 : 78 :
356 : 0 : void QgsVectorLayerTemporalProperties::setEndExpression( const QString &endExpression )
357 : : {
358 : 0 : mEndExpression = endExpression;
359 : 0 : }
360 : :
361 : 0 : bool QgsVectorLayerTemporalProperties::accumulateFeatures() const
362 : : {
363 : 0 : return mAccumulateFeatures;
364 : : }
365 : :
366 : 0 : void QgsVectorLayerTemporalProperties::setAccumulateFeatures( bool accumulateFeatures )
367 : : {
368 : 0 : mAccumulateFeatures = accumulateFeatures;
369 : 0 : }
370 : :
371 : 0 : double QgsVectorLayerTemporalProperties::fixedDuration() const
372 : : {
373 : 0 : return mFixedDuration;
374 : : }
375 : :
376 : 0 : void QgsVectorLayerTemporalProperties::setFixedDuration( double fixedDuration )
377 : : {
378 : 0 : mFixedDuration = fixedDuration;
379 : 0 : }
380 : :
381 : 0 : QString QgsVectorLayerTemporalProperties::startField() const
382 : : {
383 : 0 : return mStartFieldName;
384 : : }
385 : :
386 : 78 : void QgsVectorLayerTemporalProperties::setStartField( const QString &startFieldName )
387 : : {
388 : 78 : mStartFieldName = startFieldName;
389 : 78 : }
390 : :
391 : 0 : QString QgsVectorLayerTemporalProperties::endField() const
392 : : {
393 : 0 : return mEndFieldName;
394 : : }
395 : :
396 : 78 : void QgsVectorLayerTemporalProperties::setEndField( const QString &field )
397 : : {
398 : 78 : mEndFieldName = field;
399 : 78 : }
400 : :
401 : 0 : QString QgsVectorLayerTemporalProperties::durationField() const
402 : : {
403 : 0 : return mDurationFieldName;
404 : : }
405 : :
406 : 0 : void QgsVectorLayerTemporalProperties::setDurationField( const QString &field )
407 : : {
408 : 0 : mDurationFieldName = field;
409 : 0 : }
410 : :
411 : 0 : QgsUnitTypes::TemporalUnit QgsVectorLayerTemporalProperties::durationUnits() const
412 : : {
413 : 0 : return mDurationUnit;
414 : : }
415 : :
416 : 0 : void QgsVectorLayerTemporalProperties::setDurationUnits( QgsUnitTypes::TemporalUnit units )
417 : : {
418 : 0 : mDurationUnit = units;
419 : 0 : }
420 : :
421 : 0 : QString dateTimeExpressionLiteral( const QDateTime &datetime )
422 : : {
423 : 0 : return QStringLiteral( "make_datetime(%1,%2,%3,%4,%5,%6)" ).arg( datetime.date().year() )
424 : 0 : .arg( datetime.date().month() )
425 : 0 : .arg( datetime.date().day() )
426 : 0 : .arg( datetime.time().hour() )
427 : 0 : .arg( datetime.time().minute() )
428 : 0 : .arg( datetime.time().second() + datetime.time().msec() / 1000.0 );
429 : 0 : }
430 : :
431 : 0 : QString QgsVectorLayerTemporalProperties::createFilterString( const QgsVectorLayerTemporalContext &, const QgsDateTimeRange &range ) const
432 : : {
433 : 0 : if ( !isActive() )
434 : 0 : return QString();
435 : :
436 : 0 : switch ( mMode )
437 : : {
438 : : case ModeFixedTemporalRange:
439 : : case ModeRedrawLayerOnly:
440 : 0 : return QString();
441 : :
442 : : case ModeFeatureDateTimeInstantFromField:
443 : : {
444 : 0 : if ( mAccumulateFeatures )
445 : : {
446 : 0 : return QStringLiteral( "(%1 %2 %3) OR %1 IS NULL" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
447 : 0 : range.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
448 : 0 : dateTimeExpressionLiteral( range.end() ) );
449 : : }
450 : 0 : else if ( qgsDoubleNear( mFixedDuration, 0.0 ) )
451 : : {
452 : 0 : return QStringLiteral( "(%1 %2 %3 AND %1 %4 %5) OR %1 IS NULL" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
453 : 0 : range.includeBeginning() ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
454 : 0 : dateTimeExpressionLiteral( range.begin() ),
455 : 0 : range.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
456 : 0 : dateTimeExpressionLiteral( range.end() ) );
457 : : }
458 : : else
459 : : {
460 : : // Working with features with events with a duration, so taking this duration into account (+ QgsInterval( -mFixedDuration, mDurationUnit ) ))
461 : : // Then we are NOT taking the range.includeBeginning() and range.includeEnd() into account (deliberately, see #38468)
462 : 0 : return QStringLiteral( "(%1 > %2 AND %1 < %3) OR %1 IS NULL" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
463 : 0 : dateTimeExpressionLiteral( range.begin() + QgsInterval( -mFixedDuration, mDurationUnit ) ),
464 : 0 : dateTimeExpressionLiteral( range.end() ) );
465 : : }
466 : : }
467 : :
468 : : case ModeFeatureDateTimeStartAndDurationFromFields:
469 : : {
470 : 0 : QString intervalExpression;
471 : 0 : switch ( mDurationUnit )
472 : : {
473 : : case QgsUnitTypes::TemporalMilliseconds:
474 : 0 : intervalExpression = QStringLiteral( "make_interval(0,0,0,0,0,0,%1/1000)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
475 : 0 : break;
476 : :
477 : : case QgsUnitTypes::TemporalSeconds:
478 : 0 : intervalExpression = QStringLiteral( "make_interval(0,0,0,0,0,0,%1)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
479 : 0 : break;
480 : :
481 : : case QgsUnitTypes::TemporalMinutes:
482 : 0 : intervalExpression = QStringLiteral( "make_interval(0,0,0,0,0,%1,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
483 : 0 : break;
484 : :
485 : : case QgsUnitTypes::TemporalHours:
486 : 0 : intervalExpression = QStringLiteral( "make_interval(0,0,0,0,%1,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
487 : 0 : break;
488 : :
489 : : case QgsUnitTypes::TemporalDays:
490 : 0 : intervalExpression = QStringLiteral( "make_interval(0,0,0,%1,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
491 : 0 : break;
492 : :
493 : : case QgsUnitTypes::TemporalWeeks:
494 : 0 : intervalExpression = QStringLiteral( "make_interval(0,0,%1,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
495 : 0 : break;
496 : :
497 : : case QgsUnitTypes::TemporalMonths:
498 : 0 : intervalExpression = QStringLiteral( "make_interval(0,%1,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
499 : 0 : break;
500 : :
501 : : case QgsUnitTypes::TemporalYears:
502 : 0 : intervalExpression = QStringLiteral( "make_interval(%1,0,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
503 : 0 : break;
504 : :
505 : : case QgsUnitTypes::TemporalDecades:
506 : 0 : intervalExpression = QStringLiteral( "make_interval(10 * %1,0,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
507 : 0 : break;
508 : :
509 : : case QgsUnitTypes::TemporalCenturies:
510 : 0 : intervalExpression = QStringLiteral( "make_interval(100 * %1,0,0,0,0,0,0)" ).arg( QgsExpression::quotedColumnRef( mDurationFieldName ) );
511 : 0 : break;
512 : :
513 : : case QgsUnitTypes::TemporalUnknownUnit:
514 : : case QgsUnitTypes::TemporalIrregularStep:
515 : 0 : return QString();
516 : : }
517 : 0 : return QStringLiteral( "(%1 %2 %3 OR %1 IS NULL) AND ((%1 + %4 %5 %6) OR %7 IS NULL)" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
518 : 0 : range.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
519 : 0 : dateTimeExpressionLiteral( range.end() ),
520 : : intervalExpression,
521 : 0 : range.includeBeginning() ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
522 : 0 : dateTimeExpressionLiteral( range.begin() ),
523 : 0 : QgsExpression::quotedColumnRef( mDurationFieldName ) );
524 : : break;
525 : 0 : }
526 : :
527 : : case ModeFeatureDateTimeStartAndEndFromFields:
528 : : {
529 : 0 : if ( !mStartFieldName.isEmpty() && !mEndFieldName.isEmpty() )
530 : : {
531 : 0 : return QStringLiteral( "(%1 %2 %3 OR %1 IS NULL) AND (%4 %5 %6 OR %4 IS NULL)" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
532 : 0 : range.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
533 : 0 : dateTimeExpressionLiteral( range.end() ),
534 : 0 : QgsExpression::quotedColumnRef( mEndFieldName ),
535 : 0 : range.includeBeginning() ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
536 : 0 : dateTimeExpressionLiteral( range.begin() ) );
537 : : }
538 : 0 : else if ( !mStartFieldName.isEmpty() )
539 : : {
540 : 0 : return QStringLiteral( "%1 %2 %3 OR %1 IS NULL" ).arg( QgsExpression::quotedColumnRef( mStartFieldName ),
541 : 0 : range.includeBeginning() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
542 : 0 : dateTimeExpressionLiteral( range.end() ) );
543 : : }
544 : 0 : else if ( !mEndFieldName.isEmpty() )
545 : : {
546 : 0 : return QStringLiteral( "%1 %2 %3 OR %1 IS NULL" ).arg( QgsExpression::quotedColumnRef( mEndFieldName ),
547 : 0 : range.includeBeginning() ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
548 : 0 : dateTimeExpressionLiteral( range.begin() ) );
549 : : }
550 : 0 : break;
551 : : }
552 : :
553 : : case ModeFeatureDateTimeStartAndEndFromExpressions:
554 : : {
555 : 0 : if ( !mStartExpression.isEmpty() && !mEndExpression.isEmpty() )
556 : : {
557 : 0 : return QStringLiteral( "((%1) %2 %3) AND ((%4) %5 %6)" ).arg( mStartExpression,
558 : 0 : range.includeEnd() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
559 : 0 : dateTimeExpressionLiteral( range.end() ),
560 : 0 : mEndExpression,
561 : 0 : range.includeBeginning() ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
562 : 0 : dateTimeExpressionLiteral( range.begin() ) );
563 : : }
564 : 0 : else if ( !mStartExpression.isEmpty() )
565 : : {
566 : 0 : return QStringLiteral( "(%1) %2 %3" ).arg( mStartExpression,
567 : 0 : range.includeBeginning() ? QStringLiteral( "<=" ) : QStringLiteral( "<" ),
568 : 0 : dateTimeExpressionLiteral( range.end() ) );
569 : : }
570 : 0 : else if ( !mEndExpression.isEmpty() )
571 : : {
572 : 0 : return QStringLiteral( "(%1) %2 %3" ).arg( mEndExpression,
573 : 0 : range.includeBeginning() ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
574 : 0 : dateTimeExpressionLiteral( range.begin() ) );
575 : : }
576 : 0 : break;
577 : : }
578 : : }
579 : :
580 : 0 : return QString();
581 : 0 : }
582 : :
583 : 78 : void QgsVectorLayerTemporalProperties::guessDefaultsFromFields( const QgsFields &fields )
584 : : {
585 : :
586 : : // Check the fields and keep the first one that matches.
587 : : // We assume that the user has organized the data with the
588 : : // more "interesting" field names first.
589 : : // This candidates list is a prioritized list of candidates ranked by "interestingness"!
590 : : // See discussion at https://github.com/qgis/QGIS/pull/30245 - this list must NOT be translated,
591 : : // but adding hardcoded localized variants of the strings is encouraged.
592 : 84 : static QStringList sStartCandidates{ QStringLiteral( "start" ),
593 : 4 : QStringLiteral( "begin" ),
594 : 4 : QStringLiteral( "from" )};
595 : :
596 : 84 : static QStringList sEndCandidates{ QStringLiteral( "end" ),
597 : 4 : QStringLiteral( "last" ),
598 : 4 : QStringLiteral( "to" )};
599 : :
600 : 80 : static QStringList sSingleFieldCandidates{ QStringLiteral( "event" ) };
601 : :
602 : :
603 : 78 : bool foundStart = false;
604 : 78 : bool foundEnd = false;
605 : :
606 : 162 : for ( const QgsField &field : fields )
607 : : {
608 : 84 : if ( field.type() != QVariant::Date && field.type() != QVariant::DateTime )
609 : 84 : continue;
610 : :
611 : 0 : if ( !foundStart )
612 : : {
613 : 0 : for ( const QString &candidate : sStartCandidates )
614 : : {
615 : 0 : QString fldName = field.name();
616 : 0 : if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
617 : : {
618 : 0 : mStartFieldName = fldName;
619 : 0 : foundStart = true;
620 : 0 : }
621 : 0 : }
622 : 0 : }
623 : :
624 : 0 : if ( !foundEnd )
625 : : {
626 : 0 : for ( const QString &candidate : sEndCandidates )
627 : : {
628 : 0 : QString fldName = field.name();
629 : 0 : if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
630 : : {
631 : 0 : mEndFieldName = fldName;
632 : 0 : foundEnd = true;
633 : 0 : }
634 : 0 : }
635 : 0 : }
636 : :
637 : 0 : if ( foundStart && foundEnd )
638 : 0 : break;
639 : : }
640 : :
641 : 78 : if ( !foundStart )
642 : : {
643 : : // loop again, looking for likely "single field" candidates
644 : 162 : for ( const QgsField &field : fields )
645 : : {
646 : 84 : if ( field.type() != QVariant::Date && field.type() != QVariant::DateTime )
647 : 84 : continue;
648 : :
649 : 0 : for ( const QString &candidate : sSingleFieldCandidates )
650 : : {
651 : 0 : QString fldName = field.name();
652 : 0 : if ( fldName.indexOf( candidate, 0, Qt::CaseInsensitive ) > -1 )
653 : : {
654 : 0 : mStartFieldName = fldName;
655 : 0 : foundStart = true;
656 : 0 : }
657 : 0 : }
658 : :
659 : 0 : if ( foundStart )
660 : 0 : break;
661 : : }
662 : 78 : }
663 : :
664 : 78 : if ( foundStart && foundEnd )
665 : 0 : mMode = ModeFeatureDateTimeStartAndEndFromFields;
666 : 78 : else if ( foundStart )
667 : 0 : mMode = ModeFeatureDateTimeInstantFromField;
668 : :
669 : : // note -- NEVER auto enable temporal properties here! It's just a helper designed
670 : : // to shortcut the initial field selection
671 : 78 : }
672 : :
673 : 0 : QgsVectorLayer *QgsVectorLayerTemporalContext::layer() const
674 : : {
675 : 0 : return mLayer;
676 : : }
677 : :
678 : 0 : void QgsVectorLayerTemporalContext::setLayer( QgsVectorLayer *layer )
679 : : {
680 : 0 : mLayer = layer;
681 : 0 : }
|