QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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  QVector<double> sorted;
81 
82  // if we have lots of values, we need to take a random sample
83  if ( values.size() > mMaximumSize )
84  {
85  // for now, sample at least maximumSize values or a 10% sample, whichever
86  // is larger. This will produce a more representative sample for very large
87  // layers, but could end up being computationally intensive...
88 
89  sample.resize( std::max( mMaximumSize, static_cast<int>( values.size() ) / 10 ) );
90 
91  QgsDebugMsgLevel( QStringLiteral( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ), 2 );
92  QgsDebugMsgLevel( QStringLiteral( "values:%1" ).arg( values.size() ), 2 );
93 
94  sample[ 0 ] = minimum;
95  sample[ 1 ] = maximum;
96 
97  sorted = values.toVector();
98  std::sort( sorted.begin(), sorted.end() );
99 
100  int j = -1;
101 
102  // loop through all values in initial array
103  // skip the first value as it is a minimum one
104  // skip the last value as that one is a maximum one
105  // and those are already in the sample as items 0 and 1
106  for ( int i = 1; i < sorted.size() - 2; i++ )
107  {
108  if ( ( i * ( mMaximumSize - 2 ) / ( sorted.size() - 2 ) ) > j )
109  {
110  j++;
111  sample[ j + 2 ] = sorted[ i ];
112  }
113  }
114  }
115  else
116  {
117  sample = values.toVector();
118  }
119 
120  const int n = sample.size();
121 
122  // sort the sample values
123  std::sort( sample.begin(), sample.end() );
124 
125  QVector< QVector<int> > matrixOne( n + 1 );
126  QVector< QVector<double> > matrixTwo( n + 1 );
127 
128  for ( int i = 0; i <= n; i++ )
129  {
130  matrixOne[i].resize( nclasses + 1 );
131  matrixTwo[i].resize( nclasses + 1 );
132  }
133 
134  for ( int i = 1; i <= nclasses; i++ )
135  {
136  matrixOne[0][i] = 1;
137  matrixOne[1][i] = 1;
138  matrixTwo[0][i] = 0.0;
139  for ( int j = 2; j <= n; j++ )
140  {
141  matrixTwo[j][i] = std::numeric_limits<double>::max();
142  }
143  }
144 
145  for ( int l = 2; l <= n; l++ )
146  {
147  double s1 = 0.0;
148  double s2 = 0.0;
149  int w = 0;
150 
151  double v = 0.0;
152 
153  for ( int m = 1; m <= l; m++ )
154  {
155  const int i3 = l - m + 1;
156 
157  const double val = sample[ i3 - 1 ];
158 
159  s2 += val * val;
160  s1 += val;
161  w++;
162 
163  v = s2 - ( s1 * s1 ) / static_cast< double >( w );
164  const int i4 = i3 - 1;
165  if ( i4 != 0 )
166  {
167  for ( int j = 2; j <= nclasses; j++ )
168  {
169  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
170  {
171  matrixOne[l][j] = i4;
172  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
173  }
174  }
175  }
176  }
177  matrixOne[l][1] = 1;
178  matrixTwo[l][1] = v;
179  }
180 
181  QVector<double> breaks( nclasses );
182  breaks[nclasses - 1] = sample[n - 1];
183 
184  for ( int j = nclasses, k = n; j >= 2; j-- )
185  {
186  const int id = matrixOne[k][j] - 1;
187  breaks[j - 2] = sample[id];
188  k = matrixOne[k][j] - 1;
189  }
190 
191  return breaks.toList();
192 }
QgsClassificationMethod
QgsClassificationMethod is an abstract class for implementations of classification methods.
Definition: qgsclassificationmethod.h:88
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:28
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Definition: qgsapplication.cpp:693
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