Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsrange.h
3 : : ----------
4 : : begin : April 2017
5 : : copyright : (C) 2017 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 : : #ifndef QGSRANGE_H
19 : : #define QGSRANGE_H
20 : :
21 : : #include "qgis_sip.h"
22 : : #include "qgis_core.h"
23 : : #include "qgis.h"
24 : :
25 : : #include <QDate>
26 : : #include <QDateTime>
27 : :
28 : : /**
29 : : * \class QgsRange
30 : : * \ingroup core
31 : : * \brief A template based class for storing ranges (lower to upper values).
32 : : *
33 : : * QgsRange classes represent a range of values of some element type. For instance,
34 : : * ranges of int might be used to represent integer ranges.
35 : : *
36 : : * Ranges can indicate whether the upper and lower values are inclusive or exclusive.
37 : : * The inclusivity or exclusivity of bounds is considered when determining things like
38 : : * whether ranges overlap or during calculation of range intersections.
39 : : *
40 : : * \see QgsDoubleRange
41 : : * \see QgsIntRange
42 : : * \note not available in Python bindings (but class provided for template-based inheritance)
43 : : * \since QGIS 3.0
44 : : */
45 : : template <typename T>
46 : : class QgsRange
47 : : {
48 : : public:
49 : :
50 : : /**
51 : : * Constructor for QgsRange. The \a lower and \a upper bounds are specified,
52 : : * and optionally whether or not these bounds are included in the range.
53 : : */
54 : 6 : QgsRange( T lower, T upper, bool includeLower = true, bool includeUpper = true )
55 : 6 : : mLower( lower )
56 : 6 : , mUpper( upper )
57 : 6 : , mIncludeLower( includeLower )
58 : 6 : , mIncludeUpper( includeUpper )
59 : 6 : {}
60 : :
61 : : /**
62 : : * Returns the lower bound of the range.
63 : : * \see upper()
64 : : * \see includeLower()
65 : : */
66 : 0 : T lower() const { return mLower; }
67 : :
68 : : /**
69 : : * Returns the upper bound of the range.
70 : : * \see lower()
71 : : * \see includeUpper()
72 : : */
73 : 0 : T upper() const { return mUpper; }
74 : :
75 : : /**
76 : : * Returns TRUE if the lower bound is inclusive, or FALSE if the lower
77 : : * bound is exclusive.
78 : : * \see lower()
79 : : * \see includeUpper()
80 : : */
81 : : bool includeLower() const { return mIncludeLower; }
82 : :
83 : : /**
84 : : * Returns TRUE if the upper bound is inclusive, or FALSE if the upper
85 : : * bound is exclusive.
86 : : * \see upper()
87 : : * \see includeLower()
88 : : */
89 : : bool includeUpper() const { return mIncludeUpper; }
90 : :
91 : : /**
92 : : * Returns TRUE if the range is empty, ie the lower bound equals (or exceeds) the upper bound
93 : : * and either the bounds are exclusive.
94 : : * \see isSingleton()
95 : : */
96 : : bool isEmpty() const { return mLower > mUpper || ( mUpper == mLower && !( mIncludeLower || mIncludeUpper ) ); }
97 : :
98 : : /**
99 : : * Returns TRUE if the range consists only of a single value or instant.
100 : : * \see isEmpty()
101 : : */
102 : : bool isSingleton() const { return mLower == mUpper && ( mIncludeLower || mIncludeUpper ); }
103 : :
104 : : /**
105 : : * Returns TRUE if this range contains another range.
106 : : * \see overlaps()
107 : : */
108 : : bool contains( const QgsRange<T> &other ) const
109 : : {
110 : : bool lowerOk = ( mIncludeLower && mLower <= other.mLower )
111 : : || ( !mIncludeLower && mLower < other.mLower )
112 : : || ( !mIncludeLower && !other.mIncludeLower && mLower <= other.mLower );
113 : : if ( !lowerOk )
114 : : return false;
115 : :
116 : : bool upperOk = ( mIncludeUpper && mUpper >= other.mUpper )
117 : : || ( !mIncludeUpper && mUpper > other.mUpper )
118 : : || ( !mIncludeUpper && !other.mIncludeUpper && mUpper >= other.mUpper );
119 : : if ( !upperOk )
120 : : return false;
121 : :
122 : : return true;
123 : : }
124 : :
125 : : /**
126 : : * Returns TRUE if this range contains a specified \a element.
127 : : */
128 : 0 : bool contains( T element ) const
129 : : {
130 : 0 : bool lowerOk = ( mIncludeLower && mLower <= element )
131 : 0 : || ( !mIncludeLower && mLower < element );
132 : 0 : if ( !lowerOk )
133 : 0 : return false;
134 : :
135 : 0 : bool upperOk = ( mIncludeUpper && mUpper >= element )
136 : 0 : || ( !mIncludeUpper && mUpper > element );
137 : 0 : if ( !upperOk )
138 : 0 : return false;
139 : :
140 : 0 : return true;
141 : 0 : }
142 : :
143 : : /**
144 : : * Returns TRUE if this range overlaps another range.
145 : : * \see contains()
146 : : */
147 : 0 : bool overlaps( const QgsRange<T> &other ) const
148 : : {
149 : 0 : if ( ( ( mIncludeLower && mLower <= other.mLower ) || ( !mIncludeLower && mLower < other.mLower ) )
150 : 0 : && ( ( mIncludeUpper && mUpper >= other.mUpper ) || ( !mIncludeUpper && mUpper > other.mUpper ) ) )
151 : 0 : return true;
152 : :
153 : 0 : if ( ( ( mIncludeLower && mLower <= other.mLower ) || ( !mIncludeLower && mLower < other.mLower ) )
154 : 0 : && ( ( mIncludeUpper && mUpper >= other.mLower ) || ( !mIncludeUpper && mUpper > other.mLower ) ) )
155 : 0 : return true;
156 : :
157 : 0 : if ( ( ( mIncludeLower && mLower <= other.mUpper ) || ( !mIncludeLower && mLower < other.mUpper ) )
158 : 0 : && ( ( mIncludeUpper && mUpper >= other.mUpper ) || ( !mIncludeUpper && mUpper > other.mUpper ) ) )
159 : 0 : return true;
160 : :
161 : 0 : if ( ( ( mIncludeLower && mLower >= other.mLower ) || ( !mIncludeLower && mLower > other.mLower ) )
162 : 0 : && ( ( mIncludeLower && mLower <= other.mUpper ) || ( !mIncludeLower && mLower < other.mUpper ) ) )
163 : 0 : return true;
164 : :
165 : 0 : if ( mLower == other.mLower && mUpper == other.mUpper )
166 : 0 : return true;
167 : :
168 : 0 : return false;
169 : 0 : }
170 : :
171 : : bool operator==( const QgsRange<T> &other ) const
172 : : {
173 : : return mLower == other.mLower &&
174 : : mUpper == other.mUpper &&
175 : : mIncludeLower == other.includeLower() &&
176 : : mIncludeUpper == other.includeUpper();
177 : : }
178 : :
179 : : bool operator!=( const QgsRange<T> &other ) const
180 : : {
181 : : return ( ! operator==( other ) );
182 : : }
183 : :
184 : : protected:
185 : :
186 : : T mLower;
187 : : T mUpper;
188 : : bool mIncludeLower = true;
189 : : bool mIncludeUpper = true;
190 : :
191 : : };
192 : :
193 : :
194 : : /**
195 : : * \brief QgsRange which stores a range of double values.
196 : : * \ingroup core
197 : : * \see QgsIntRange
198 : : * \see QgsDateRange
199 : : * \see QgsDateTimeRange
200 : : * \since QGIS 3.0
201 : : */
202 : : class CORE_EXPORT QgsDoubleRange : public QgsRange< double >
203 : : {
204 : : public:
205 : :
206 : : #ifndef SIP_RUN
207 : :
208 : : /**
209 : : * Constructor for QgsDoubleRange. The \a lower and \a upper bounds are specified,
210 : : * and optionally whether or not these bounds are included in the range.
211 : : *
212 : : * The default values for \a lower and \a upper construct an infinite range (see isInfinite()).
213 : : *
214 : : * \since QGIS 3.18
215 : : */
216 : 6 : QgsDoubleRange( double lower = std::numeric_limits< double >::lowest(),
217 : : double upper = std::numeric_limits< double >::max(),
218 : : bool includeLower = true, bool includeUpper = true )
219 : 6 : : QgsRange( lower, upper, includeLower, includeUpper )
220 : 12 : {}
221 : : #else
222 : :
223 : : /**
224 : : * Constructor for QgsDoubleRange. The \a lower and \a upper bounds are specified,
225 : : * and optionally whether or not these bounds are included in the range.
226 : : */
227 : : QgsDoubleRange( double lower,
228 : : double upper,
229 : : bool includeLower = true, bool includeUpper = true )
230 : : : QgsRange( lower, upper, includeLower, includeUpper )
231 : : {}
232 : :
233 : : /**
234 : : * Constructor for QgsDoubleRange containing an infinite range (see isInfinite()).
235 : : *
236 : : * \since QGIS 3.18
237 : : */
238 : : QgsDoubleRange()
239 : : : QgsRange( std::numeric_limits< double >::lowest(), std::numeric_limits< double >::max(), true, true )
240 : : {}
241 : : #endif
242 : :
243 : : /**
244 : : * Returns TRUE if the range consists of all possible values.
245 : : * \since QGIS 3.18
246 : : */
247 : 0 : bool isInfinite() const
248 : : {
249 : 0 : return lower() == std::numeric_limits< double >::lowest() && upper() == std::numeric_limits< double >::max();
250 : : }
251 : :
252 : : #ifdef SIP_RUN
253 : : SIP_PYOBJECT __repr__();
254 : : % MethodCode
255 : : QString str = QStringLiteral( "<QgsDoubleRange: %1%2, %3%4>" ).arg( sipCpp->includeLower() ? QStringLiteral( "[" ) : QStringLiteral( "(" ) )
256 : : .arg( sipCpp->lower() )
257 : : .arg( sipCpp->upper() )
258 : : .arg( sipCpp->includeUpper() ? QStringLiteral( "]" ) : QStringLiteral( ")" ) );
259 : : sipRes = PyUnicode_FromString( str.toUtf8().constData() );
260 : : % End
261 : : #endif
262 : :
263 : : bool operator==( const QgsDoubleRange &other ) const
264 : : {
265 : : return qgsDoubleNear( mLower, other.mLower ) &&
266 : : qgsDoubleNear( mUpper, other.mUpper ) &&
267 : : mIncludeLower == other.includeLower() &&
268 : : mIncludeUpper == other.includeUpper();
269 : : }
270 : :
271 : : bool operator!=( const QgsDoubleRange &other ) const
272 : : {
273 : : return ( ! operator==( other ) );
274 : : }
275 : :
276 : : };
277 : :
278 : :
279 : : /**
280 : : * \brief QgsRange which stores a range of integer values.
281 : : * \ingroup core
282 : : * \see QgsDoubleRange
283 : : * \see QgsDateRange
284 : : * \see QgsDateTimeRange
285 : : * \since QGIS 3.0
286 : : */
287 : : class CORE_EXPORT QgsIntRange : public QgsRange< int >
288 : : {
289 : : public:
290 : :
291 : : #ifndef SIP_RUN
292 : :
293 : : /**
294 : : * Constructor for QgsIntRange. The \a lower and \a upper bounds are specified,
295 : : * and optionally whether or not these bounds are included in the range.
296 : : *
297 : : * The default values for \a lower and \a upper construct an infinite range (see isInfinite()).
298 : : *
299 : : * \since QGIS 3.18
300 : : */
301 : : QgsIntRange( int lower = std::numeric_limits< int >::lowest(),
302 : : int upper = std::numeric_limits< int >::max(),
303 : : bool includeLower = true, bool includeUpper = true )
304 : : : QgsRange( lower, upper, includeLower, includeUpper )
305 : : {}
306 : : #else
307 : :
308 : : /**
309 : : * Constructor for QgsIntRange. The \a lower and \a upper bounds are specified,
310 : : * and optionally whether or not these bounds are included in the range.
311 : : */
312 : : QgsIntRange( int lower,
313 : : int upper,
314 : : bool includeLower = true, bool includeUpper = true )
315 : : : QgsRange( lower, upper, includeLower, includeUpper )
316 : : {}
317 : :
318 : : /**
319 : : * Constructor for QgsIntRange containing an infinite range (see isInfinite()).
320 : : *
321 : : * \since QGIS 3.18
322 : : */
323 : : QgsIntRange()
324 : : : QgsRange( std::numeric_limits< int >::lowest(), std::numeric_limits< int >::max(), true, true )
325 : : {}
326 : : #endif
327 : :
328 : : /**
329 : : * Returns TRUE if the range consists of all possible values.
330 : : * \since QGIS 3.18
331 : : */
332 : : bool isInfinite() const
333 : : {
334 : : return lower() == std::numeric_limits< int >::lowest() && upper() == std::numeric_limits< int >::max();
335 : : }
336 : :
337 : : #ifdef SIP_RUN
338 : : SIP_PYOBJECT __repr__();
339 : : % MethodCode
340 : : QString str = QStringLiteral( "<QgsIntRange: %1%2, %3%4>" ).arg( sipCpp->includeLower() ? QStringLiteral( "[" ) : QStringLiteral( "(" ) )
341 : : .arg( sipCpp->lower() )
342 : : .arg( sipCpp->upper() )
343 : : .arg( sipCpp->includeUpper() ? QStringLiteral( "]" ) : QStringLiteral( ")" ) );
344 : : sipRes = PyUnicode_FromString( str.toUtf8().constData() );
345 : : % End
346 : : #endif
347 : :
348 : : };
349 : :
350 : :
351 : : /**
352 : : * \class QgsTemporalRange
353 : : * \ingroup core
354 : : * \brief A template based class for storing temporal ranges (beginning to end values).
355 : : *
356 : : * QgsTemporalRange classes represent a range of values of some temporal type. For instance,
357 : : * ranges of QDateTime might be used to represent datetime ranges.
358 : : *
359 : : * Ranges can indicate whether the upper and lower values are inclusive or exclusive.
360 : : * The inclusivity or exclusivity of bounds is considered when determining things like
361 : : * whether ranges overlap or during calculation of range intersections.
362 : : *
363 : : * \see QgsDateRange
364 : : * \note not available in Python bindings (but class provided for template-based inheritance)
365 : : * \since QGIS 3.0
366 : : */
367 : : template <typename T>
368 : 227 : class QgsTemporalRange
369 : : {
370 : : public:
371 : :
372 : : /**
373 : : * Constructor for QgsTemporalRange. The \a begin and \a end are specified,
374 : : * and optionally whether or not these bounds are included in the range.
375 : : * \note in Python \a begin and \a end must be provided.
376 : : */
377 : : #ifndef SIP_RUN
378 : 175 : QgsTemporalRange( const T &begin = T(), const T &end = T(), bool includeBeginning = true, bool includeEnd = true )
379 : 175 : : mLower( begin )
380 : 175 : , mUpper( end )
381 : 175 : , mIncludeLower( includeBeginning )
382 : 175 : , mIncludeUpper( includeEnd )
383 : 175 : {}
384 : : #else
385 : : QgsTemporalRange( const T &begin, const T &end, bool includeBeginning = true, bool includeEnd = true );
386 : : // default constructor as default value for templates is not handled in SIP
387 : : #endif
388 : :
389 : : /**
390 : : * Returns the beginning of the range.
391 : : * \see end()
392 : : * \see includeBeginning()
393 : : */
394 : 0 : T begin() const { return mLower; }
395 : :
396 : : /**
397 : : * Returns the upper bound of the range.
398 : : * \see begin()
399 : : * \see includeEnd()
400 : : */
401 : 0 : T end() const { return mUpper; }
402 : :
403 : : /**
404 : : * Returns TRUE if the beginning is inclusive, or FALSE if the beginning
405 : : * is exclusive.
406 : : * \see begin()
407 : : * \see includeEnd()
408 : : */
409 : 0 : bool includeBeginning() const { return mIncludeLower; }
410 : :
411 : : /**
412 : : * Returns TRUE if the end is inclusive, or FALSE if the end is exclusive.
413 : : * \see end()
414 : : * \see includeBeginning()
415 : : */
416 : 0 : bool includeEnd() const { return mIncludeUpper; }
417 : :
418 : : /**
419 : : * Returns TRUE if the range consists only of a single instant.
420 : : * \see isEmpty()
421 : : * \see isInfinite()
422 : : */
423 : 0 : bool isInstant() const { return mLower.isValid() && mUpper.isValid() && mLower == mUpper && ( mIncludeLower || mIncludeUpper ); }
424 : :
425 : : /**
426 : : * Returns TRUE if the range consists of all possible values.
427 : : * \see isEmpty()
428 : : * \see isInstant()
429 : : */
430 : 0 : bool isInfinite() const
431 : : {
432 : 0 : return !mLower.isValid() && !mUpper.isValid();
433 : : }
434 : :
435 : : /**
436 : : * Returns TRUE if the range is empty, ie the beginning equals (or exceeds) the end
437 : : * and either of the bounds are exclusive.
438 : : * A range with both invalid beginning and end is considered infinite and not empty.
439 : : */
440 : 0 : bool isEmpty() const
441 : : {
442 : 0 : if ( !mLower.isValid() && !mUpper.isValid() )
443 : 0 : return false;
444 : :
445 : 0 : if ( mLower.isValid() != mUpper.isValid() )
446 : 0 : return false;
447 : :
448 : 0 : if ( mLower > mUpper )
449 : 0 : return true;
450 : :
451 : 0 : if ( mLower == mUpper && !( mIncludeLower || mIncludeUpper ) )
452 : 0 : return true;
453 : :
454 : 0 : return false;
455 : 0 : }
456 : :
457 : : /**
458 : : * Returns TRUE if this range contains another range.
459 : : */
460 : : bool contains( const QgsTemporalRange<T> &other ) const
461 : : {
462 : : if ( !other.mLower.isValid() && mLower.isValid() )
463 : : return false;
464 : :
465 : : if ( mLower.isValid() )
466 : : {
467 : : bool lowerOk = ( mIncludeLower && mLower <= other.mLower )
468 : : || ( !mIncludeLower && mLower < other.mLower )
469 : : || ( !mIncludeLower && !other.mIncludeLower && mLower <= other.mLower );
470 : : if ( !lowerOk )
471 : : return false;
472 : : }
473 : :
474 : : if ( !other.mUpper.isValid() && mUpper.isValid() )
475 : : return false;
476 : :
477 : : if ( mUpper.isValid() )
478 : : {
479 : : bool upperOk = ( mIncludeUpper && mUpper >= other.mUpper )
480 : : || ( !mIncludeUpper && mUpper > other.mUpper )
481 : : || ( !mIncludeUpper && !other.mIncludeUpper && mUpper >= other.mUpper );
482 : : if ( !upperOk )
483 : : return false;
484 : : }
485 : :
486 : : return true;
487 : : }
488 : :
489 : : /**
490 : : * Returns TRUE if this range contains a specified \a element.
491 : : */
492 : 0 : bool contains( const T &element ) const
493 : : {
494 : 0 : if ( !element.isValid() )
495 : 0 : return false;
496 : :
497 : 0 : if ( mLower.isValid() )
498 : : {
499 : 0 : bool lowerOk = ( mIncludeLower && mLower <= element )
500 : 0 : || ( !mIncludeLower && mLower < element );
501 : 0 : if ( !lowerOk )
502 : 0 : return false;
503 : 0 : }
504 : :
505 : 0 : if ( mUpper.isValid() )
506 : : {
507 : 0 : bool upperOk = ( mIncludeUpper && mUpper >= element )
508 : 0 : || ( !mIncludeUpper && mUpper > element );
509 : 0 : if ( !upperOk )
510 : 0 : return false;
511 : 0 : }
512 : :
513 : 0 : return true;
514 : 0 : }
515 : :
516 : : /**
517 : : * Returns TRUE if this range overlaps another range.
518 : : */
519 : 0 : bool overlaps( const QgsTemporalRange<T> &other ) const
520 : : {
521 : 0 : if ( !mUpper.isValid() && ( ( mIncludeLower && mLower <= other.mUpper ) || ( !mIncludeLower && mLower < other.mUpper ) ) )
522 : 0 : return true;
523 : :
524 : 0 : if ( ( ( mIncludeLower && mLower <= other.mLower ) || ( !mIncludeLower && mLower < other.mLower ) )
525 : 0 : && ( ( mIncludeUpper && mUpper >= other.mUpper ) || ( !mIncludeUpper && mUpper > other.mUpper ) ) )
526 : 0 : return true;
527 : :
528 : 0 : if ( ( ( mIncludeLower && mLower <= other.mLower ) || ( !mIncludeLower && mLower < other.mLower ) )
529 : 0 : && ( ( mIncludeUpper && mUpper >= other.mLower ) || ( !mIncludeUpper && mUpper > other.mLower ) ) )
530 : 0 : return true;
531 : :
532 : 0 : if ( ( ( mIncludeLower && mLower <= other.mUpper ) || ( !mIncludeLower && mLower < other.mUpper ) )
533 : 0 : && ( ( mIncludeUpper && mUpper >= other.mUpper ) || ( !mIncludeUpper && mUpper > other.mUpper ) ) )
534 : 0 : return true;
535 : :
536 : 0 : if ( ( ( mIncludeLower && mLower >= other.mLower ) || ( !mIncludeLower && mLower > other.mLower ) )
537 : 0 : && ( ( mIncludeLower && mLower <= other.mUpper ) || ( !mIncludeLower && mLower < other.mUpper ) ) )
538 : 0 : return true;
539 : :
540 : 0 : if ( mLower == other.mLower && mUpper == other.mUpper )
541 : 0 : return true;
542 : :
543 : 0 : return false;
544 : 0 : }
545 : :
546 : : /**
547 : : * Extends the range in place by extending this range out to include an \a other range.
548 : : * If \a other is empty the range is not changed.
549 : : * If the range is empty and \a other is not, the range is changed and set to \a other.
550 : : * \see isEmpty()
551 : : * \returns TRUE if the range was extended
552 : : * \since QGIS 3.12
553 : : */
554 : 0 : bool extend( const QgsTemporalRange<T> &other )
555 : : {
556 : 0 : if ( other.isEmpty() )
557 : : {
558 : 0 : return false;
559 : : }
560 : 0 : else if ( isEmpty() )
561 : : {
562 : 0 : mLower = other.begin();
563 : 0 : mUpper = other.end();
564 : 0 : mIncludeLower = other.includeBeginning();
565 : 0 : mIncludeUpper = other.includeEnd();
566 : 0 : return true;
567 : : }
568 : :
569 : : // Both not empty, do some math
570 : 0 : bool changed { false };
571 : :
572 : : // Lower
573 : 0 : if ( ! other.begin().isValid()
574 : 0 : || ( begin().isValid() && other.begin() < mLower ) )
575 : : {
576 : 0 : mLower = other.begin();
577 : 0 : mIncludeLower = other.includeBeginning();
578 : 0 : changed = true;
579 : 0 : }
580 : 0 : else if ( other.begin() == mLower && other.includeBeginning() && ! mIncludeLower )
581 : : {
582 : 0 : mIncludeLower = true;
583 : 0 : changed = true;
584 : 0 : }
585 : :
586 : : // Upper
587 : 0 : if ( ! other.end().isValid()
588 : 0 : || ( end().isValid() && other.end() > mUpper ) )
589 : : {
590 : 0 : mUpper = other.end();
591 : 0 : mIncludeUpper = other.includeEnd();
592 : 0 : changed = true;
593 : 0 : }
594 : 0 : else if ( other.end() == mUpper && other.includeEnd() && ! mIncludeUpper )
595 : : {
596 : 0 : mIncludeUpper = true;
597 : 0 : changed = true;
598 : 0 : }
599 : 0 : return changed;
600 : 0 : }
601 : :
602 : : #ifndef SIP_RUN
603 : :
604 : : /**
605 : : * Merges a list of temporal ranges.
606 : : *
607 : : * Any overlapping ranges will be converted to a single range which covers the entire
608 : : * range of the input ranges.
609 : : *
610 : : * The returned value will be a list of non-contiguous ranges which completely encompass
611 : : * the input \a ranges, sorted in ascending order.
612 : : *
613 : : * \note Not available in Python bindings
614 : : *
615 : : * \since QGIS 3.20
616 : : */
617 : 0 : static QList< QgsTemporalRange<T> > mergeRanges( const QList< QgsTemporalRange<T> > &ranges )
618 : : {
619 : 0 : if ( ranges.empty() )
620 : 0 : return {};
621 : :
622 : 0 : QList< QgsTemporalRange<T > > sortedRanges = ranges;
623 : 0 : std::sort( sortedRanges.begin(), sortedRanges.end(), []( const QgsTemporalRange< T > &a, const QgsTemporalRange< T > &b ) -> bool { return a.begin() < b.begin(); } );
624 : 0 : QList< QgsTemporalRange<T>> res;
625 : 0 : res.reserve( sortedRanges.size() );
626 : :
627 : 0 : QgsTemporalRange<T> prevRange;
628 : 0 : auto it = sortedRanges.constBegin();
629 : 0 : prevRange = *it++;
630 : 0 : for ( ; it != sortedRanges.constEnd(); ++it )
631 : : {
632 : 0 : if ( prevRange.overlaps( *it ) )
633 : : {
634 : 0 : prevRange.extend( *it );
635 : 0 : }
636 : : else
637 : : {
638 : 0 : res << prevRange;
639 : 0 : prevRange = *it;
640 : : }
641 : 0 : }
642 : 0 : res << prevRange;
643 : 0 : return res;
644 : 0 : }
645 : : #endif
646 : :
647 : 0 : bool operator==( const QgsTemporalRange<T> &other ) const
648 : : {
649 : 0 : return mLower == other.mLower &&
650 : 0 : mUpper == other.mUpper &&
651 : 0 : mIncludeLower == other.includeBeginning() &&
652 : 0 : mIncludeUpper == other.includeEnd();
653 : : }
654 : :
655 : : bool operator!=( const QgsTemporalRange<T> &other ) const
656 : : {
657 : : return ( ! operator==( other ) );
658 : : }
659 : :
660 : : private:
661 : :
662 : : T mLower;
663 : : T mUpper;
664 : : bool mIncludeLower = true;
665 : : bool mIncludeUpper = true;
666 : : };
667 : :
668 : :
669 : : /**
670 : : * QgsRange which stores a range of dates.
671 : : *
672 : : * Invalid QDates as the beginning or end are permitted. In this case,
673 : : * the bound is considered to be infinite. E.g. QgsDateRange(QDate(),QDate(2017,1,1))
674 : : * is treated as a range containing all dates before 2017-1-1.
675 : : * QgsDateRange(QDate(2017,1,1),QDate()) is treated as a range containing all dates after 2017-1-1.
676 : : * \see QgsDateTimeRange
677 : : * \since QGIS 3.0
678 : : */
679 : : typedef QgsTemporalRange< QDate > QgsDateRange SIP_DOC_TEMPLATE;
680 : :
681 : 5 : Q_DECLARE_METATYPE( QgsDateRange )
682 : :
683 : : /**
684 : : * QgsRange which stores a range of date times.
685 : : *
686 : : * Invalid QDateTimes as the beginning or end are permitted. In this case,
687 : : * the bound is considered to be infinite. E.g. QgsDateTimeRange(QDateTime(),QDateTime(2017,1,1))
688 : : * is treated as a range containing all dates before 2017-1-1.
689 : : * QgsDateTimeRange(QDateTime(2017,1,1),QDateTime()) is treated as a range containing all dates after 2017-1-1.
690 : : * \see QgsDateRange
691 : : * \since QGIS 3.0
692 : : */
693 : : typedef QgsTemporalRange< QDateTime > QgsDateTimeRange SIP_DOC_TEMPLATE;
694 : :
695 : 10 : Q_DECLARE_METATYPE( QgsDateTimeRange )
696 : :
697 : : #endif // QGSRANGE_H
|