Skip to content
Permalink
Browse files
Merge pull request #42657 from tomass/master
Prepare Jenks set without using randomisation
  • Loading branch information
elpaso committed Apr 11, 2021
2 parents 3d249a8 + 1793deb commit 393d60bf25651f6973d853c754a2d8a1abe60d1f
Showing with 45 additions and 12 deletions.
  1. +16 −11 src/core/classification/qgsclassificationjenks.cpp
  2. +29 −1 tests/src/python/test_qgsclassificationmethod.py
@@ -77,6 +77,7 @@ QList<double> QgsClassificationJenks::calculateBreaks( double &minimum, double &
}

QVector<double> sample;
QVector<double> sorted;

// if we have lots of values, we need to take a random sample
if ( values.size() > mMaximumSize )
@@ -93,18 +94,22 @@ QList<double> QgsClassificationJenks::calculateBreaks( double &minimum, double &
sample[ 0 ] = minimum;
sample[ 1 ] = maximum;

for ( int i = 2; i < sample.size(); i++ )
{
// pick a random integer from 0 to n
sorted = values.toVector();
std::sort( sorted.begin(), sorted.end() );

#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
double r = QRandomGenerator::global()->generate();
int j = std::floor( r / QRandomGenerator::max() * ( values.size() - 1 ) );
#else
double r = qrand();
int j = std::floor( r / RAND_MAX * ( values.size() - 1 ) );
#endif
sample[ i ] = values[ j ];
int j = -1;

// loop through all values in initial array
// skip the first value as it is a minimum one
// skip the last value as that one is a maximum one
// and those are already in the sample as items 0 and 1
for ( int i = 1; i < sorted.size() - 2; i++ )
{
if ( ( i * ( mMaximumSize - 2 ) / ( sorted.size() - 2 ) ) > j )
{
j++;
sample[ j + 2 ] = sorted[ i ];
}
}
}
else
@@ -11,10 +11,11 @@
__copyright__ = 'Copyright 2019, The QGIS Project'

import qgis # NOQA
import random

from qgis.PyQt.QtCore import QLocale
from qgis.testing import unittest, start_app
from qgis.core import QgsClassificationMethod, QgsClassificationLogarithmic, QgsFeature, QgsVectorLayer, QgsPointXY, \
from qgis.core import QgsClassificationMethod, QgsClassificationLogarithmic, QgsClassificationJenks, QgsFeature, QgsVectorLayer, QgsPointXY, \
QgsGeometry

start_app()
@@ -85,6 +86,33 @@ def testQgsClassificationLogarithmic_FilterZeroNeg(self):
self.assertEqual(r[0].label(), '-2 - 10^0')
self.assertEqual(QgsClassificationMethod.rangesToBreaks(r), [1.0, 10.0, 100.0, 1000.0, 10000.0])

def testQgsClassificationJenksSimple(self):
# This is a simple Natural Breaks Jenks test checking if simple calculation can be done
# when number of values is below 3000 (value hardcoded in qgsclassificationjenks.h)
# And if returned values are consistent (the same as at the time of creation of this test)
values = [-33, -41, -43, 16, 29, 9, -35, 57, 26, -30]
vl = createMemoryLayer(values)
m = QgsClassificationJenks()

r = m.classes(vl, 'value', 4)
self.assertEqual(len(r), 4)
self.assertEqual(QgsClassificationMethod.rangesToBreaks(r),
[-30.0, 16.0, 29.0, 57.0])

def testQgsClassificationJenksHighNumber(self):
# This test checks if Jenkis classification does not crash when number of
# values in a set is higher than 3000 (value hardcoded in qgsclassificationjenks.h)
# And if returned values are consistent (the same as at the time of creation of this test)
random.seed(42 * 42)
values = [random.randint(-1000, 1000) for _ in range(5000)]
vl = createMemoryLayer(values)
m = QgsClassificationJenks()

r = m.classes(vl, 'value', 4)
self.assertEqual(len(r), 4)
self.assertEqual(QgsClassificationMethod.rangesToBreaks(r),
[-506.0, -4.0, 499.0, 1000.0])


if __name__ == "__main__":
unittest.main()

0 comments on commit 393d60b

Please sign in to comment.