QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsclassificationjenks.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsclassificationjenks.h
3  ---------------------
4  begin : September 2019
5  copyright : (C) 2019 by Denis Rouzaud
6  email : [email protected]
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 
16 #include <limits>
17 #include "qgsclassificationjenks.h"
18 #include "qgsapplication.h"
19 
20 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
21 #include <QRandomGenerator>
22 #endif
23 
26 {
27 
28 }
29 
31 {
32  return QObject::tr( "Natural Breaks (Jenks)" );
33 }
34 
36 {
37  return QStringLiteral( "Jenks" );
38 }
39 
41 {
43  copyBase( c );
44  return c;
45 }
46 
48 {
49  return QgsApplication::getThemeIcon( "classification_methods/mClassificationNaturalBreak.svg" );
50 }
51 
52 
53 QList<double> QgsClassificationJenks::calculateBreaks( double &minimum, double &maximum,
54  const QList<double> &values, int nclasses )
55 {
56  // Jenks Optimal (Natural Breaks) algorithm
57  // Based on the Jenks algorithm from the 'classInt' package available for
58  // the R statistical prgramming language, and from Python code from here:
59  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
60  // and is based on a JAVA and Fortran code available here:
61  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
62 
63  // Returns class breaks such that classes are internally homogeneous while
64  // assuring heterogeneity among classes.
65 
66  if ( values.isEmpty() )
67  return QList<double>();
68 
69  if ( nclasses <= 1 )
70  {
71  return QList<double>() << maximum;
72  }
73 
74  if ( nclasses >= values.size() )
75  {
76  return values;
77  }
78 
79  QVector<double> sample;
80 
81  // if we have lots of values, we need to take a random sample
82  if ( values.size() > mMaximumSize )
83  {
84  // for now, sample at least maximumSize values or a 10% sample, whichever
85  // is larger. This will produce a more representative sample for very large
86  // layers, but could end up being computationally intensive...
87 
88  sample.resize( std::max( mMaximumSize, values.size() / 10 ) );
89 
90  QgsDebugMsgLevel( QStringLiteral( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ), 2 );
91  QgsDebugMsgLevel( QStringLiteral( "values:%1" ).arg( values.size() ), 2 );
92 
93  sample[ 0 ] = minimum;
94  sample[ 1 ] = maximum;
95 
96  for ( int i = 2; i < sample.size(); i++ )
97  {
98  // pick a random integer from 0 to n
99 
100 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
101  double r = QRandomGenerator::global()->generate();
102 #else
103  double r = qrand();
104 #endif
105  int j = std::floor( r / RAND_MAX * ( values.size() - 1 ) );
106  sample[ i ] = values[ j ];
107  }
108  }
109  else
110  {
111  sample = values.toVector();
112  }
113 
114  int n = sample.size();
115 
116  // sort the sample values
117  std::sort( sample.begin(), sample.end() );
118 
119  QVector< QVector<int> > matrixOne( n + 1 );
120  QVector< QVector<double> > matrixTwo( n + 1 );
121 
122  for ( int i = 0; i <= n; i++ )
123  {
124  matrixOne[i].resize( nclasses + 1 );
125  matrixTwo[i].resize( nclasses + 1 );
126  }
127 
128  for ( int i = 1; i <= nclasses; i++ )
129  {
130  matrixOne[0][i] = 1;
131  matrixOne[1][i] = 1;
132  matrixTwo[0][i] = 0.0;
133  for ( int j = 2; j <= n; j++ )
134  {
135  matrixTwo[j][i] = std::numeric_limits<double>::max();
136  }
137  }
138 
139  for ( int l = 2; l <= n; l++ )
140  {
141  double s1 = 0.0;
142  double s2 = 0.0;
143  int w = 0;
144 
145  double v = 0.0;
146 
147  for ( int m = 1; m <= l; m++ )
148  {
149  int i3 = l - m + 1;
150 
151  double val = sample[ i3 - 1 ];
152 
153  s2 += val * val;
154  s1 += val;
155  w++;
156 
157  v = s2 - ( s1 * s1 ) / static_cast< double >( w );
158  int i4 = i3 - 1;
159  if ( i4 != 0 )
160  {
161  for ( int j = 2; j <= nclasses; j++ )
162  {
163  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
164  {
165  matrixOne[l][j] = i4;
166  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
167  }
168  }
169  }
170  }
171  matrixOne[l][1] = 1;
172  matrixTwo[l][1] = v;
173  }
174 
175  QVector<double> breaks( nclasses );
176  breaks[nclasses - 1] = sample[n - 1];
177 
178  for ( int j = nclasses, k = n; j >= 2; j-- )
179  {
180  int id = matrixOne[k][j] - 1;
181  breaks[j - 2] = sample[id];
182  k = matrixOne[k][j] - 1;
183  }
184 
185  return breaks.toList();
186 }
QgsClassificationMethod
QgsClassificationMethod is an abstract class for implementations of classification methods.
Definition: qgsclassificationmethod.h:88
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:626
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsClassificationJenks::name
QString name() const override
The readable and translate name of the method.
Definition: qgsclassificationjenks.cpp:30
qgsapplication.h
QgsClassificationJenks::QgsClassificationJenks
QgsClassificationJenks()
Definition: qgsclassificationjenks.cpp:24
qgsclassificationjenks.h
QgsClassificationJenks::icon
QIcon icon() const override
The icon of the method.
Definition: qgsclassificationjenks.cpp:47
QgsClassificationMethod::copyBase
void copyBase(QgsClassificationMethod *c) const
Copy the parameters (shall be used in clone implementation)
Definition: qgsclassificationmethod.cpp:52
c
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Definition: porting_processing.dox:1
QgsClassificationJenks
QgsClassificationJenks is an implementation of QgsClassificationMethod for natural breaks based on Je...
Definition: qgsclassificationjenks.h:29
QgsClassificationJenks::id
QString id() const override
The id of the method as saved in the project, must be unique in registry.
Definition: qgsclassificationjenks.cpp:35
QgsClassificationJenks::clone
QgsClassificationMethod * clone() const override
Returns a clone of the method.
Definition: qgsclassificationjenks.cpp:40