Skip to content

Commit c4ea048

Browse files
committedApr 18, 2017
New QgsRange template based class for storing interval ranges
QgsRange classes represent a range of values of some element type. For instance, ranges of QDateTime might be used to represent the ranges of timestamp ranges. Ranges can indicate whether the upper and lower values are inclusive or exclusive. The inclusivity or exclusivity of bounds is considered when determining things like whether ranges overlap or during calculation of range intersections. Includes typedefs for QgsDoubleRange, QgsIntRange, QgsDateRange.
1 parent 33b6c41 commit c4ea048

File tree

7 files changed

+687
-0
lines changed

7 files changed

+687
-0
lines changed
 

‎python/auto_sip.blacklist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ core/qgspropertycollection.sip
9494
core/qgsprovidermetadata.sip
9595
core/qgsproviderregistry.sip
9696
core/qgspythonrunner.sip
97+
core/qgsrange.sip
9798
core/qgsrelation.sip
9899
core/qgsrelationmanager.sip
99100
core/qgsrenderchecker.sip

‎python/core/core.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
%Include qgsprovidermetadata.sip
128128
%Include qgsproviderregistry.sip
129129
%Include qgspythonrunner.sip
130+
%Include qgsrange.sip
130131
%Include qgsrelation.sip
131132
%Include qgsrelationmanager.sip
132133
%Include qgsrenderchecker.sip

‎python/core/qgsrange.sip

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
class QgsDoubleRange
2+
{
3+
%TypeHeaderCode
4+
#include <qgsrange.h>
5+
%End
6+
7+
public:
8+
9+
QgsDoubleRange( double lower, double upper, bool includeLower = true, bool includeUpper = true );
10+
double lower() const;
11+
double upper() const;
12+
bool includeLower() const;
13+
bool includeUpper() const;
14+
bool isEmpty() const;
15+
bool contains( const QgsDoubleRange &other ) const;
16+
bool contains( double element ) const;
17+
bool overlaps( const QgsDoubleRange &other ) const;
18+
};
19+
20+
class QgsIntRange
21+
{
22+
%TypeHeaderCode
23+
#include <qgsrange.h>
24+
%End
25+
26+
public:
27+
28+
QgsIntRange( int lower, int upper, bool includeLower = true, bool includeUpper = true );
29+
int lower() const;
30+
int upper() const;
31+
bool includeLower() const;
32+
bool includeUpper() const;
33+
bool isEmpty() const;
34+
bool contains( const QgsIntRange &other ) const;
35+
bool contains( int element ) const;
36+
bool overlaps( const QgsIntRange &other ) const;
37+
};
38+
39+
class QgsDateRange
40+
{
41+
%TypeHeaderCode
42+
#include <qgsrange.h>
43+
%End
44+
45+
public:
46+
47+
QgsDateRange( QDate lower, QDate upper, bool includeLower = true, bool includeUpper = true );
48+
QDate lower() const;
49+
QDate upper() const;
50+
bool includeLower() const;
51+
bool includeUpper() const;
52+
bool isEmpty() const;
53+
bool contains( const QgsDateRange &other ) const;
54+
bool contains( QDate element ) const;
55+
bool overlaps( const QgsDateRange &other ) const;
56+
};
57+

‎src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,7 @@ SET(QGIS_CORE_HDRS
762762
qgsprovidermetadata.h
763763
qgsproviderregistry.h
764764
qgspythonrunner.h
765+
qgsrange.h
765766
qgsrenderchecker.h
766767
qgsrendercontext.h
767768
qgsruntimeprofiler.h

‎src/core/qgsrange.h

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
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.h"
22+
#include "qgis_core.h"
23+
24+
/**
25+
* \class QgsRange
26+
* \ingroup core
27+
* A template based class for storing ranges (lower to upper values).
28+
*
29+
* QgsRange classes represent a range of values of some element type. For instance,
30+
* ranges of QDateTime might be used to represent the ranges of timestamp ranges.
31+
*
32+
* Ranges can indicate whether the upper and lower values are inclusive or exclusive.
33+
* The inclusivity or exclusivity of bounds is considered when determining things like
34+
* whether ranges overlap or during calculation of range intersections.
35+
*
36+
* \since QGIS 3.0
37+
* \see QgsDoubleRange
38+
* \see QgsIntRange
39+
* \note not available in Python bindings
40+
*/
41+
template <class T> class CORE_EXPORT QgsRange
42+
{
43+
public:
44+
45+
/**
46+
* Constructor for QgsRange. The \a lower and \a upper bounds are specified,
47+
* and optionally whether or not these bounds are included in the range.
48+
*/
49+
QgsRange( T lower, T upper, bool includeLower = true, bool includeUpper = true )
50+
: mLower( lower )
51+
, mUpper( upper )
52+
, mIncludeLower( includeLower )
53+
, mIncludeUpper( includeUpper )
54+
{}
55+
56+
/**
57+
* Returns the lower bound of the range.
58+
* \see upper()
59+
* \see includeLower()
60+
*/
61+
T lower() const { return mLower; }
62+
63+
/**
64+
* Returns the upper bound of the range.
65+
* \see lower()
66+
* \see includeUpper()
67+
*/
68+
T upper() const { return mUpper; }
69+
70+
/**
71+
* Returns true if the lower bound is inclusive, or false if the lower
72+
* bound is exclusive.
73+
* \see lower()
74+
* \see includeUpper()
75+
*/
76+
bool includeLower() const { return mIncludeLower; }
77+
78+
/**
79+
* Returns true if the upper bound is inclusive, or false if the upper
80+
* bound is exclusive.
81+
* \see upper()
82+
* \see includeLower()
83+
*/
84+
bool includeUpper() const { return mIncludeUpper; }
85+
86+
/**
87+
* Returns true if the range is empty, ie the lower bound equals (or exceeds) the upper bound
88+
* and either the bounds are exclusive.
89+
*/
90+
bool isEmpty() const { return mLower > mUpper || ( mUpper == mLower && !( mIncludeLower || mIncludeUpper ) ); }
91+
92+
/**
93+
* Returns true if this range contains another range.
94+
* \see overlaps()
95+
*/
96+
bool contains( const QgsRange<T> &other ) const
97+
{
98+
bool lowerOk = ( mIncludeLower && mLower <= other.mLower )
99+
|| ( !mIncludeLower && mLower < other.mLower )
100+
|| ( !mIncludeLower && !other.mIncludeLower && mLower <= other.mLower );
101+
if ( !lowerOk )
102+
return false;
103+
104+
bool upperOk = ( mIncludeUpper && mUpper >= other.mUpper )
105+
|| ( !mIncludeUpper && mUpper > other.mUpper )
106+
|| ( !mIncludeUpper && !other.mIncludeUpper && mUpper >= other.mUpper );
107+
if ( !upperOk )
108+
return false;
109+
110+
return true;
111+
}
112+
113+
/**
114+
* Returns true if this range contains a specified \a element.
115+
*/
116+
bool contains( T element ) const
117+
{
118+
bool lowerOk = ( mIncludeLower && mLower <= element )
119+
|| ( !mIncludeLower && mLower < element );
120+
if ( !lowerOk )
121+
return false;
122+
123+
bool upperOk = ( mIncludeUpper && mUpper >= element )
124+
|| ( !mIncludeUpper && mUpper > element );
125+
if ( !upperOk )
126+
return false;
127+
128+
return true;
129+
}
130+
131+
/**
132+
* Returns true if this range overlaps another range.
133+
* \see contains()
134+
*/
135+
bool overlaps( const QgsRange<T> &other ) const
136+
{
137+
if ( ( ( mIncludeLower && mLower <= other.mLower ) || ( !mIncludeLower && mLower < other.mLower ) )
138+
&& ( ( mIncludeUpper && mUpper >= other.mUpper ) || ( !mIncludeUpper && mUpper > other.mUpper ) ) )
139+
return true;
140+
141+
if ( ( ( mIncludeLower && mLower <= other.mLower ) || ( !mIncludeLower && mLower < other.mLower ) )
142+
&& ( ( mIncludeUpper && mUpper >= other.mLower ) || ( !mIncludeUpper && mUpper > other.mLower ) ) )
143+
return true;
144+
145+
if ( ( ( mIncludeLower && mLower <= other.mUpper ) || ( !mIncludeLower && mLower < other.mUpper ) )
146+
&& ( ( mIncludeUpper && mUpper >= other.mUpper ) || ( !mIncludeUpper && mUpper > other.mUpper ) ) )
147+
return true;
148+
149+
if ( ( ( mIncludeLower && mLower >= other.mLower ) || ( !mIncludeLower && mLower > other.mLower ) )
150+
&& ( ( mIncludeLower && mLower <= other.mUpper ) || ( !mIncludeLower && mLower < other.mUpper ) ) )
151+
return true;
152+
153+
if ( mLower == other.mLower && mUpper == other.mUpper )
154+
return true;
155+
156+
return false;
157+
}
158+
159+
160+
private:
161+
162+
T mLower;
163+
T mUpper;
164+
bool mIncludeLower = true;
165+
bool mIncludeUpper = true;
166+
167+
};
168+
169+
170+
/**
171+
* QgsRange which stores a range of double values.
172+
* \since QGIS 3.0
173+
* \see QgsIntRange
174+
* \see QgsDateRange
175+
* \see QgsDateTimeRange
176+
*/
177+
typedef QgsRange< double > QgsDoubleRange;
178+
179+
/**
180+
* QgsRange which stores a range of integer values.
181+
* \since QGIS 3.0
182+
* \see QgsDoubleRange
183+
* \see QgsDateRange
184+
* \see QgsDateTimeRange
185+
*/
186+
typedef QgsRange< int > QgsIntRange;
187+
188+
189+
// specialization required to handle invalid QDate bounds
190+
template<>
191+
bool QgsRange<QDate>::isEmpty() const
192+
{
193+
if ( !mLower.isValid() && !mUpper.isValid() )
194+
return true;
195+
196+
if ( mLower.isValid() != mUpper.isValid() )
197+
return false;
198+
199+
if ( mLower > mUpper )
200+
return true;
201+
202+
if ( mLower == mUpper && !( mIncludeLower || mIncludeUpper ) )
203+
return true;
204+
205+
return false;
206+
}
207+
208+
template<>
209+
bool QgsRange<QDate>::contains( const QgsRange<QDate> &other ) const
210+
{
211+
if ( !other.mLower.isValid() && mLower.isValid() )
212+
return false;
213+
214+
if ( mLower.isValid() )
215+
{
216+
bool lowerOk = ( mIncludeLower && mLower <= other.mLower )
217+
|| ( !mIncludeLower && mLower < other.mLower )
218+
|| ( !mIncludeLower && !other.mIncludeLower && mLower <= other.mLower );
219+
if ( !lowerOk )
220+
return false;
221+
}
222+
223+
if ( !other.mUpper.isValid() && mUpper.isValid() )
224+
return false;
225+
226+
if ( mUpper.isValid() )
227+
{
228+
bool upperOk = ( mIncludeUpper && mUpper >= other.mUpper )
229+
|| ( !mIncludeUpper && mUpper > other.mUpper )
230+
|| ( !mIncludeUpper && !other.mIncludeUpper && mUpper >= other.mUpper );
231+
if ( !upperOk )
232+
return false;
233+
}
234+
235+
return true;
236+
}
237+
238+
template<>
239+
bool QgsRange<QDate>::contains( QDate element ) const
240+
{
241+
if ( !element.isValid() )
242+
return false;
243+
244+
if ( mLower.isValid() )
245+
{
246+
bool lowerOk = ( mIncludeLower && mLower <= element )
247+
|| ( !mIncludeLower && mLower < element );
248+
if ( !lowerOk )
249+
return false;
250+
}
251+
252+
if ( mUpper.isValid() )
253+
{
254+
bool upperOk = ( mIncludeUpper && mUpper >= element )
255+
|| ( !mIncludeUpper && mUpper > element );
256+
if ( !upperOk )
257+
return false;
258+
}
259+
260+
return true;
261+
}
262+
263+
template<>
264+
bool QgsRange<QDate>::overlaps( const QgsRange<QDate> &other ) const
265+
{
266+
if ( !mUpper.isValid() && ( ( mIncludeLower && mLower <= other.mUpper ) || ( !mIncludeLower && mLower < other.mUpper ) ) )
267+
return true;
268+
269+
if ( ( ( mIncludeLower && mLower <= other.mLower ) || ( !mIncludeLower && mLower < other.mLower ) )
270+
&& ( ( mIncludeUpper && mUpper >= other.mUpper ) || ( !mIncludeUpper && mUpper > other.mUpper ) ) )
271+
return true;
272+
273+
if ( ( ( mIncludeLower && mLower <= other.mLower ) || ( !mIncludeLower && mLower < other.mLower ) )
274+
&& ( ( mIncludeUpper && mUpper >= other.mLower ) || ( !mIncludeUpper && mUpper > other.mLower ) ) )
275+
return true;
276+
277+
if ( ( ( mIncludeLower && mLower <= other.mUpper ) || ( !mIncludeLower && mLower < other.mUpper ) )
278+
&& ( ( mIncludeUpper && mUpper >= other.mUpper ) || ( !mIncludeUpper && mUpper > other.mUpper ) ) )
279+
return true;
280+
281+
if ( ( ( mIncludeLower && mLower >= other.mLower ) || ( !mIncludeLower && mLower > other.mLower ) )
282+
&& ( ( mIncludeLower && mLower <= other.mUpper ) || ( !mIncludeLower && mLower < other.mUpper ) ) )
283+
return true;
284+
285+
if ( mLower == other.mLower && mUpper == other.mUpper )
286+
return true;
287+
288+
return false;
289+
}
290+
291+
/**
292+
* QgsRange which stores a range of dates.
293+
*
294+
* Invalid QDates as the lower or upper bound are permitted. In this case,
295+
* the bound is considered to be infinite. E.g. QgsDateRange(QDate(),QDate(2017,1,1))
296+
* is treated as a range containing all dates before 2017-1-1.
297+
* QgsDateRange(QDate(2017,1,1),QDate()) is treated as a range containing all dates after 2017-1-1.
298+
* \since QGIS 3.0
299+
* \see QgsIntRange
300+
* \see QgsDoubleRange
301+
* \see QgsDateTimeRange
302+
*/
303+
typedef QgsRange< QDate > QgsDateRange;
304+
305+
306+
307+
#endif // QGSRANGE_H

‎tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ ADD_PYTHON_TEST(PyQgsPoint test_qgspoint.py)
9898
ADD_PYTHON_TEST(PyQgsPointClusterRenderer test_qgspointclusterrenderer.py)
9999
ADD_PYTHON_TEST(PyQgsPointDisplacementRenderer test_qgspointdisplacementrenderer.py)
100100
ADD_PYTHON_TEST(PyQgsProjectionSelectionWidgets test_qgsprojectionselectionwidgets.py)
101+
ADD_PYTHON_TEST(PyQgsRange test_qgsrange.py)
101102
ADD_PYTHON_TEST(PyQgsRangeWidgets test_qgsrangewidgets.py)
102103
ADD_PYTHON_TEST(PyQgsRasterFileWriter test_qgsrasterfilewriter.py)
103104
ADD_PYTHON_TEST(PyQgsRasterFileWriterTask test_qgsrasterfilewritertask.py)

‎tests/src/python/test_qgsrange.py

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS Unit tests for QgsRange
3+
4+
.. note:: This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
"""
9+
__author__ = 'Nyall Dawson'
10+
__date__ = '11.04.2017'
11+
__copyright__ = 'Copyright 2017, The QGIS Project'
12+
# This will get replaced with a git SHA1 when you do a git archive
13+
__revision__ = '$Format:%H$'
14+
15+
import qgis # NOQA
16+
17+
from qgis.testing import unittest
18+
from qgis.core import (QgsIntRange,
19+
QgsDateRange)
20+
from qgis.PyQt.QtCore import QDate
21+
22+
23+
class TestQgsIntRange(unittest.TestCase):
24+
25+
def testGetters(self):
26+
range = QgsIntRange(1, 11)
27+
self.assertEqual(range.lower(), 1)
28+
self.assertEqual(range.upper(), 11)
29+
self.assertTrue(range.includeLower())
30+
self.assertTrue(range.includeUpper())
31+
32+
range = QgsIntRange(-1, 3, False, False)
33+
self.assertEqual(range.lower(), -1)
34+
self.assertEqual(range.upper(), 3)
35+
self.assertFalse(range.includeLower())
36+
self.assertFalse(range.includeUpper())
37+
38+
def testIsEmpty(self):
39+
range = QgsIntRange(1, 1)
40+
# should not be empty because 1 is included
41+
self.assertFalse(range.isEmpty())
42+
43+
range = QgsIntRange(1, 1, False, False)
44+
# should be empty because 1 is NOT included
45+
self.assertTrue(range.isEmpty())
46+
47+
# invalid range is empty
48+
range = QgsIntRange(1, -1)
49+
self.assertTrue(range.isEmpty())
50+
51+
def testContains(self):
52+
# includes both ends
53+
range = QgsIntRange(0, 10)
54+
self.assertTrue(range.contains(QgsIntRange(1, 9)))
55+
self.assertTrue(range.contains(QgsIntRange(1, 10)))
56+
self.assertTrue(range.contains(QgsIntRange(0, 9)))
57+
self.assertTrue(range.contains(QgsIntRange(0, 10)))
58+
self.assertFalse(range.contains(QgsIntRange(-1, 9)))
59+
self.assertFalse(range.contains(QgsIntRange(1, 11)))
60+
61+
# does not include left end
62+
range = QgsIntRange(0, 10, False, True)
63+
self.assertTrue(range.contains(QgsIntRange(1, 9)))
64+
self.assertTrue(range.contains(QgsIntRange(1, 10)))
65+
self.assertFalse(range.contains(QgsIntRange(0, 9)))
66+
self.assertFalse(range.contains(QgsIntRange(0, 10)))
67+
self.assertFalse(range.contains(QgsIntRange(-1, 9)))
68+
self.assertFalse(range.contains(QgsIntRange(1, 11)))
69+
70+
# does not include right end
71+
range = QgsIntRange(0, 10, True, False)
72+
self.assertTrue(range.contains(QgsIntRange(1, 9)))
73+
self.assertFalse(range.contains(QgsIntRange(1, 10)))
74+
self.assertTrue(range.contains(QgsIntRange(0, 9)))
75+
self.assertFalse(range.contains(QgsIntRange(0, 10)))
76+
self.assertFalse(range.contains(QgsIntRange(-1, 9)))
77+
self.assertFalse(range.contains(QgsIntRange(1, 11)))
78+
79+
def testContainsElement(self):
80+
# includes both ends
81+
range = QgsIntRange(0, 10)
82+
self.assertTrue(range.contains(0))
83+
self.assertTrue(range.contains(5))
84+
self.assertTrue(range.contains(10))
85+
self.assertFalse(range.contains(-1))
86+
self.assertFalse(range.contains(11))
87+
88+
# includes left end
89+
range = QgsIntRange(0, 10, True, False)
90+
self.assertTrue(range.contains(0))
91+
self.assertTrue(range.contains(5))
92+
self.assertFalse(range.contains(10))
93+
self.assertFalse(range.contains(-1))
94+
self.assertFalse(range.contains(11))
95+
96+
# includes right end
97+
range = QgsIntRange(0, 10, False, True)
98+
self.assertFalse(range.contains(0))
99+
self.assertTrue(range.contains(5))
100+
self.assertTrue(range.contains(10))
101+
self.assertFalse(range.contains(-1))
102+
self.assertFalse(range.contains(11))
103+
104+
# includes neither end
105+
range = QgsIntRange(0, 10, False, False)
106+
self.assertFalse(range.contains(0))
107+
self.assertTrue(range.contains(5))
108+
self.assertFalse(range.contains(10))
109+
self.assertFalse(range.contains(-1))
110+
self.assertFalse(range.contains(11))
111+
112+
def testOverlaps(self):
113+
# includes both ends
114+
range = QgsIntRange(0, 10)
115+
self.assertTrue(range.overlaps(QgsIntRange(1, 9)))
116+
self.assertTrue(range.overlaps(QgsIntRange(1, 10)))
117+
self.assertTrue(range.overlaps(QgsIntRange(1, 11)))
118+
self.assertTrue(range.overlaps(QgsIntRange(0, 9)))
119+
self.assertTrue(range.overlaps(QgsIntRange(0, 10)))
120+
self.assertTrue(range.overlaps(QgsIntRange(-1, 10)))
121+
self.assertTrue(range.overlaps(QgsIntRange(-1, 9)))
122+
self.assertTrue(range.overlaps(QgsIntRange(1, 11)))
123+
self.assertTrue(range.overlaps(QgsIntRange(-1, 11)))
124+
self.assertTrue(range.overlaps(QgsIntRange(10, 11)))
125+
self.assertTrue(range.overlaps(QgsIntRange(-1, 0)))
126+
self.assertFalse(range.overlaps(QgsIntRange(-10, -1)))
127+
self.assertFalse(range.overlaps(QgsIntRange(11, 12)))
128+
129+
# includes left end
130+
range = QgsIntRange(0, 10, True, False)
131+
self.assertTrue(range.overlaps(QgsIntRange(1, 9)))
132+
self.assertTrue(range.overlaps(QgsIntRange(1, 10)))
133+
self.assertTrue(range.overlaps(QgsIntRange(1, 11)))
134+
self.assertTrue(range.overlaps(QgsIntRange(0, 9)))
135+
self.assertTrue(range.overlaps(QgsIntRange(0, 10)))
136+
self.assertTrue(range.overlaps(QgsIntRange(-1, 10)))
137+
self.assertTrue(range.overlaps(QgsIntRange(-1, 9)))
138+
self.assertTrue(range.overlaps(QgsIntRange(1, 11)))
139+
self.assertTrue(range.overlaps(QgsIntRange(-1, 11)))
140+
self.assertFalse(range.overlaps(QgsIntRange(10, 11)))
141+
self.assertTrue(range.overlaps(QgsIntRange(-1, 0)))
142+
self.assertFalse(range.overlaps(QgsIntRange(-10, -1)))
143+
self.assertFalse(range.overlaps(QgsIntRange(11, 12)))
144+
145+
# includes right end
146+
range = QgsIntRange(0, 10, False, True)
147+
self.assertTrue(range.overlaps(QgsIntRange(1, 9)))
148+
self.assertTrue(range.overlaps(QgsIntRange(1, 10)))
149+
self.assertTrue(range.overlaps(QgsIntRange(1, 11)))
150+
self.assertTrue(range.overlaps(QgsIntRange(0, 9)))
151+
self.assertTrue(range.overlaps(QgsIntRange(0, 10)))
152+
self.assertTrue(range.overlaps(QgsIntRange(-1, 10)))
153+
self.assertTrue(range.overlaps(QgsIntRange(-1, 9)))
154+
self.assertTrue(range.overlaps(QgsIntRange(1, 11)))
155+
self.assertTrue(range.overlaps(QgsIntRange(-1, 11)))
156+
self.assertTrue(range.overlaps(QgsIntRange(10, 11)))
157+
self.assertFalse(range.overlaps(QgsIntRange(-1, 0)))
158+
self.assertFalse(range.overlaps(QgsIntRange(-10, -1)))
159+
self.assertFalse(range.overlaps(QgsIntRange(11, 12)))
160+
161+
# includes neither end
162+
range = QgsIntRange(0, 10, False, False)
163+
self.assertTrue(range.overlaps(QgsIntRange(1, 9)))
164+
self.assertTrue(range.overlaps(QgsIntRange(1, 10)))
165+
self.assertTrue(range.overlaps(QgsIntRange(1, 11)))
166+
self.assertTrue(range.overlaps(QgsIntRange(0, 9)))
167+
self.assertTrue(range.overlaps(QgsIntRange(0, 10)))
168+
self.assertTrue(range.overlaps(QgsIntRange(-1, 10)))
169+
self.assertTrue(range.overlaps(QgsIntRange(-1, 9)))
170+
self.assertTrue(range.overlaps(QgsIntRange(1, 11)))
171+
self.assertTrue(range.overlaps(QgsIntRange(-1, 11)))
172+
self.assertFalse(range.overlaps(QgsIntRange(10, 11)))
173+
self.assertFalse(range.overlaps(QgsIntRange(-1, 0)))
174+
self.assertFalse(range.overlaps(QgsIntRange(-10, -1)))
175+
self.assertFalse(range.overlaps(QgsIntRange(11, 12)))
176+
177+
178+
class TestQgsDateRange(unittest.TestCase):
179+
180+
def testGetters(self):
181+
range = QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2))
182+
self.assertEqual(range.lower(), QDate(2010, 3, 1))
183+
self.assertEqual(range.upper(), QDate(2010, 6, 2))
184+
self.assertTrue(range.includeLower())
185+
self.assertTrue(range.includeUpper())
186+
187+
range = QgsDateRange(QDate(), QDate(2010, 6, 2))
188+
self.assertFalse(range.lower().isValid())
189+
self.assertEqual(range.upper(), QDate(2010, 6, 2))
190+
191+
range = QgsDateRange(QDate(2010, 3, 1), QDate())
192+
self.assertEqual(range.lower(), QDate(2010, 3, 1))
193+
self.assertFalse(range.upper().isValid())
194+
195+
def testIsEmpty(self):
196+
range = QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2))
197+
self.assertFalse(range.isEmpty())
198+
199+
range = QgsDateRange(QDate(), QDate(2010, 6, 2))
200+
self.assertFalse(range.isEmpty())
201+
202+
range = QgsDateRange(QDate(2010, 3, 1), QDate())
203+
self.assertFalse(range.isEmpty())
204+
205+
range = QgsDateRange(QDate(), QDate())
206+
self.assertTrue(range.isEmpty())
207+
208+
range = QgsDateRange(QDate(2017, 3, 1), QDate(2010, 6, 2))
209+
self.assertTrue(range.isEmpty())
210+
211+
range = QgsDateRange(QDate(2010, 3, 1), QDate(2010, 3, 1))
212+
self.assertFalse(range.isEmpty())
213+
214+
range = QgsDateRange(QDate(2010, 3, 1), QDate(2010, 3, 1), False, False)
215+
self.assertTrue(range.isEmpty())
216+
217+
def testContains(self):
218+
# includes both ends
219+
range = QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2))
220+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 4, 5))))
221+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 6, 2))))
222+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 4, 5))))
223+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2))))
224+
self.assertFalse(range.contains(QgsDateRange(QDate(2009, 4, 1), QDate(2010, 4, 5))))
225+
self.assertFalse(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate(2017, 4, 5))))
226+
self.assertFalse(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate())))
227+
self.assertFalse(range.contains(QgsDateRange(QDate(), QDate(2010, 4, 1))))
228+
229+
# infinite left end
230+
range = QgsDateRange(QDate(), QDate(2010, 6, 2))
231+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 4, 5))))
232+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 6, 2))))
233+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 4, 5))))
234+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2))))
235+
self.assertTrue(range.contains(QgsDateRange(QDate(2009, 4, 1), QDate(2010, 4, 5))))
236+
self.assertFalse(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate(2017, 4, 5))))
237+
self.assertFalse(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate())))
238+
self.assertTrue(range.contains(QgsDateRange(QDate(), QDate(2010, 4, 1))))
239+
240+
# infinite right end
241+
range = QgsDateRange(QDate(2010, 3, 1), QDate())
242+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 4, 5))))
243+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 6, 2))))
244+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 4, 5))))
245+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2))))
246+
self.assertFalse(range.contains(QgsDateRange(QDate(2009, 4, 1), QDate(2010, 4, 5))))
247+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate(2017, 4, 5))))
248+
self.assertTrue(range.contains(QgsDateRange(QDate(2010, 4, 1), QDate())))
249+
self.assertFalse(range.contains(QgsDateRange(QDate(), QDate(2010, 4, 1))))
250+
251+
def testContainsElement(self):
252+
# includes both ends
253+
range = QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2))
254+
self.assertTrue(range.contains(QDate(2010, 3, 1)))
255+
self.assertTrue(range.contains(QDate(2010, 5, 2)))
256+
self.assertTrue(range.contains(QDate(2010, 6, 2)))
257+
self.assertFalse(range.contains(QDate(2009, 6, 2)))
258+
self.assertFalse(range.contains(QDate(2017, 6, 2)))
259+
self.assertFalse(range.contains(QDate()))
260+
261+
# infinite left end
262+
range = QgsDateRange(QDate(), QDate(2010, 6, 2))
263+
self.assertTrue(range.contains(QDate(2010, 3, 1)))
264+
self.assertTrue(range.contains(QDate(2010, 5, 2)))
265+
self.assertTrue(range.contains(QDate(2010, 6, 2)))
266+
self.assertTrue(range.contains(QDate(2009, 6, 2)))
267+
self.assertFalse(range.contains(QDate(2017, 6, 2)))
268+
self.assertFalse(range.contains(QDate()))
269+
270+
# infinite right end
271+
range = QgsDateRange(QDate(2010, 3, 1), QDate())
272+
self.assertTrue(range.contains(QDate(2010, 3, 1)))
273+
self.assertTrue(range.contains(QDate(2010, 5, 2)))
274+
self.assertTrue(range.contains(QDate(2010, 6, 2)))
275+
self.assertFalse(range.contains(QDate(2009, 6, 2)))
276+
self.assertTrue(range.contains(QDate(2017, 6, 2)))
277+
self.assertFalse(range.contains(QDate()))
278+
279+
def testOverlaps(self):
280+
# includes both ends
281+
range = QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2))
282+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 4, 5))))
283+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 6, 2))))
284+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 4, 5))))
285+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2))))
286+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2009, 4, 1), QDate(2010, 4, 5))))
287+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate(2017, 4, 5))))
288+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate())))
289+
self.assertTrue(range.overlaps(QgsDateRange(QDate(), QDate(2010, 4, 1))))
290+
self.assertFalse(range.overlaps(QgsDateRange(QDate(2009, 4, 1), QDate(2009, 8, 5))))
291+
self.assertFalse(range.overlaps(QgsDateRange(QDate(2019, 4, 1), QDate(2019, 8, 5))))
292+
293+
range = QgsDateRange(QDate(), QDate(2010, 6, 2))
294+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 4, 5))))
295+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 6, 2))))
296+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 4, 5))))
297+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2))))
298+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2009, 4, 1), QDate(2010, 4, 5))))
299+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate(2017, 4, 5))))
300+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate())))
301+
self.assertTrue(range.overlaps(QgsDateRange(QDate(), QDate(2010, 4, 1))))
302+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2009, 4, 1), QDate(2009, 8, 5))))
303+
self.assertFalse(range.overlaps(QgsDateRange(QDate(2019, 4, 1), QDate(2019, 8, 5))))
304+
305+
range = QgsDateRange(QDate(2010, 3, 1), QDate())
306+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 4, 5))))
307+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate(2010, 6, 2))))
308+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 4, 5))))
309+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 3, 1), QDate(2010, 6, 2))))
310+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2009, 4, 1), QDate(2010, 4, 5))))
311+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate(2017, 4, 5))))
312+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2010, 4, 1), QDate())))
313+
self.assertTrue(range.overlaps(QgsDateRange(QDate(), QDate(2010, 4, 1))))
314+
self.assertFalse(range.overlaps(QgsDateRange(QDate(2009, 4, 1), QDate(2009, 8, 5))))
315+
self.assertTrue(range.overlaps(QgsDateRange(QDate(2019, 4, 1), QDate(2019, 8, 5))))
316+
317+
318+
if __name__ == "__main__":
319+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.