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