Skip to content

Commit b7c1c3a

Browse files
committedMar 25, 2021
Add method to merge a list of possibly non-contigous date/datetime ranges
1 parent 57e5737 commit b7c1c3a

File tree

4 files changed

+141
-0
lines changed

4 files changed

+141
-0
lines changed
 

‎python/core/auto_generated/qgsrange.sip.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ If the range is empty and ``other`` is not, the range is changed and set to ``ot
375375
.. versionadded:: 3.12
376376
%End
377377

378+
378379
bool operator==( const QgsTemporalRange<T> &other ) const;
379380

380381
bool operator!=( const QgsTemporalRange<T> &other ) const;

‎src/core/qgsrange.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,53 @@ class QgsTemporalRange
599599
return changed;
600600
}
601601

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+
static QList< QgsTemporalRange<T> > mergeRanges( const QList< QgsTemporalRange<T> > &ranges )
618+
{
619+
QList< QgsTemporalRange<T > > res;
620+
621+
QList< QgsTemporalRange<T> > queue = ranges;
622+
while ( !queue.empty() )
623+
{
624+
QgsTemporalRange<T> range = queue.back();
625+
queue.pop_back();
626+
627+
bool extended = false;
628+
for ( int i = 0; i < res.count(); ++i )
629+
{
630+
if ( res[i].overlaps( range ) )
631+
{
632+
QgsTemporalRange< T > other = res.takeAt( i );
633+
other.extend( range );
634+
queue.push_back( other );
635+
extended = true;
636+
break;
637+
}
638+
}
639+
640+
if ( !extended )
641+
res.push_back( range );
642+
}
643+
644+
std::sort( res.begin(), res.end(), []( const QgsTemporalRange< T > &a, const QgsTemporalRange< T > &b ) -> bool { return a.begin() < b.begin(); } );
645+
return res;
646+
}
647+
#endif
648+
602649
bool operator==( const QgsTemporalRange<T> &other ) const
603650
{
604651
return mLower == other.mLower &&

‎tests/src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ set(TESTS
171171
testqgsproperty.cpp
172172
testqgsprovidermetadata.cpp
173173
testqgis.cpp
174+
testqgsrange.cpp
174175
testqgsrasterfilewriter.cpp
175176
testqgsrasterfill.cpp
176177
testqgsrastermarker.cpp

‎tests/src/core/testqgsrange.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/***************************************************************************
2+
testqgsrange.cpp
3+
-------------------
4+
Date : March 2021
5+
Copyright : (C) 2021 Nyall Dawson
6+
Email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
#include "qgstest.h"
16+
#include <QObject>
17+
18+
#include "qgsrange.h"
19+
20+
class TestQgsRange: public QObject
21+
{
22+
Q_OBJECT
23+
24+
private slots:
25+
void initTestCase();// will be called before the first testfunction is executed.
26+
void cleanupTestCase();// will be called after the last testfunction was executed.
27+
void init();// will be called before each testfunction is executed.
28+
void cleanup();// will be called after every testfunction.
29+
void testMergeRangesDate();
30+
void testMergeRangesDateTime();
31+
32+
private:
33+
34+
};
35+
36+
void TestQgsRange::initTestCase()
37+
{
38+
}
39+
40+
void TestQgsRange::cleanupTestCase()
41+
{
42+
}
43+
44+
void TestQgsRange::init()
45+
{
46+
47+
}
48+
49+
void TestQgsRange::cleanup()
50+
{
51+
52+
}
53+
54+
void TestQgsRange::testMergeRangesDate()
55+
{
56+
QList< QgsDateRange > ranges { QgsDateRange( QDate( 2020, 1, 10 ), QDate( 2020, 1, 15 ) ),
57+
QgsDateRange( QDate( 2020, 1, 20 ), QDate( 2020, 1, 25 ) ),
58+
QgsDateRange( QDate( 2020, 1, 9 ), QDate( 2020, 1, 11 ) ),
59+
QgsDateRange( QDate( 2020, 1, 19 ), QDate( 2020, 1, 27 ) ),
60+
QgsDateRange( QDate( 2020, 1, 1 ), QDate( 2020, 1, 3 ) ) };
61+
62+
QList< QgsDateRange > res = QgsDateRange::mergeRanges( ranges );
63+
QCOMPARE( res.size(), 3 );
64+
QCOMPARE( res.at( 0 ).begin(), QDate( 2020, 1, 1 ) );
65+
QCOMPARE( res.at( 0 ).end(), QDate( 2020, 1, 3 ) );
66+
QCOMPARE( res.at( 1 ).begin(), QDate( 2020, 1, 9 ) );
67+
QCOMPARE( res.at( 1 ).end(), QDate( 2020, 1, 15 ) );
68+
QCOMPARE( res.at( 2 ).begin(), QDate( 2020, 1, 19 ) );
69+
QCOMPARE( res.at( 2 ).end(), QDate( 2020, 1, 27 ) );
70+
}
71+
72+
void TestQgsRange::testMergeRangesDateTime()
73+
{
74+
QList< QgsDateTimeRange > ranges { QgsDateTimeRange( QDateTime( QDate( 2020, 1, 10 ), QTime( 0, 0, 0 ) ), QDateTime( QDate( 2020, 1, 15 ), QTime( 0, 0, 0 ) ) ),
75+
QgsDateTimeRange( QDateTime( QDate( 2020, 1, 20 ), QTime( 0, 0, 0 ) ), QDateTime( QDate( 2020, 1, 25 ), QTime( 0, 0, 0 ) ) ),
76+
QgsDateTimeRange( QDateTime( QDate( 2020, 1, 9 ), QTime( 0, 0, 0 ) ), QDateTime( QDate( 2020, 1, 11 ), QTime( 0, 0, 0 ) ) ),
77+
QgsDateTimeRange( QDateTime( QDate( 2020, 1, 19 ), QTime( 0, 0, 0 ) ), QDateTime( QDate( 2020, 1, 27 ), QTime( 0, 0, 0 ) ) ),
78+
QgsDateTimeRange( QDateTime( QDate( 2020, 1, 1 ), QTime( 0, 0, 0 ) ), QDateTime( QDate( 2020, 1, 3 ), QTime( 0, 0, 0 ) ) ) };
79+
80+
QList< QgsDateTimeRange > res = QgsDateTimeRange::mergeRanges( ranges );
81+
QCOMPARE( res.size(), 3 );
82+
QCOMPARE( res.at( 0 ).begin(), QDateTime( QDate( 2020, 1, 1 ), QTime( 0, 0, 0 ) ) );
83+
QCOMPARE( res.at( 0 ).end(), QDateTime( QDate( 2020, 1, 3 ), QTime( 0, 0, 0 ) ) );
84+
QCOMPARE( res.at( 1 ).begin(), QDateTime( QDate( 2020, 1, 9 ), QTime( 0, 0, 0 ) ) );
85+
QCOMPARE( res.at( 1 ).end(), QDateTime( QDate( 2020, 1, 15 ), QTime( 0, 0, 0 ) ) );
86+
QCOMPARE( res.at( 2 ).begin(), QDateTime( QDate( 2020, 1, 19 ), QTime( 0, 0, 0 ) ) );
87+
QCOMPARE( res.at( 2 ).end(), QDateTime( QDate( 2020, 1, 27 ), QTime( 0, 0, 0 ) ) );
88+
}
89+
90+
91+
QGSTEST_MAIN( TestQgsRange )
92+
#include "testqgsrange.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.