Skip to content

Commit f351c4f

Browse files
committed
[FEATURE:] Add point sample class to analysis library
1 parent b086195 commit f351c4f

8 files changed

+227
-3
lines changed

python/analysis/analysis.sip

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
%Include vector/qgsgeometryanalyzer.sip
1212
%Include vector/qgsoverlayanalyzer.sip
13+
%Include vector/qgspointsample.sip
1314
%Include vector/qgstransectsample.sip
1415
%Include vector/qgszonalstatistics.sip
1516

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/** \ingroup analysis
2+
*/
3+
4+
class QgsPointSample
5+
{
6+
%TypeHeaderCode
7+
#include <qgspointsample.h>
8+
%End
9+
10+
public:
11+
QgsPointSample( QgsVectorLayer* inputLayer, const QString& outputLayer, QString nPointsAttribute, QString minDistAttribute = QString() );
12+
~QgsPointSample();
13+
14+
/**Starts calculation of random points
15+
@return 0 in case of success*/
16+
int createRandomPoints( QProgressDialog* pd );
17+
};

src/analysis/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ SET(QGIS_ANALYSIS_SRCS
3636
raster/qgsrastermatrix.cpp
3737
vector/mersenne-twister.cpp
3838
vector/qgsgeometryanalyzer.cpp
39+
vector/qgspointsample.cpp
3940
vector/qgstransectsample.cpp
4041
vector/qgszonalstatistics.cpp
4142
vector/qgsoverlayanalyzer.cpp

src/analysis/vector/mersenne-twister.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
#include <stdio.h>
2424
#include <stdint.h>
25-
#include <limits>
2625
#include "mersenne-twister.h"
2726

2827
/*

src/analysis/vector/mersenne-twister.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#define MERSENNE_TWISTER_H
2424

2525
#include <stdint.h>
26+
#include <limits>
2627

2728
#ifdef __cplusplus
2829
extern "C" {
@@ -31,7 +32,7 @@ extern "C" {
3132
/*
3233
* Maximum number you can get from rand().
3334
*/
34-
#define RAND_MAX INT32_MAX
35+
#define RAND_MAX std::numeric_limits<int32_t>::max()
3536

3637
/*
3738
* Initialize the number generator with given seed.
+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#include "qgspointsample.h"
2+
#include "qgsgeometry.h"
3+
#include "qgsspatialindex.h"
4+
#include "qgsvectorfilewriter.h"
5+
#include "qgsvectorlayer.h"
6+
#include <QFile>
7+
#include "mersenne-twister.h"
8+
9+
10+
QgsPointSample::QgsPointSample( QgsVectorLayer* inputLayer, const QString& outputLayer, QString nPointsAttribute, QString minDistAttribute ): mInputLayer( inputLayer ),
11+
mOutputLayer( outputLayer ), mNumberOfPointsAttribute( nPointsAttribute ), mMinDistanceAttribute( minDistAttribute ), mNCreatedPoints( 0 )
12+
{
13+
}
14+
15+
QgsPointSample::QgsPointSample()
16+
{
17+
}
18+
19+
QgsPointSample::~QgsPointSample()
20+
{
21+
}
22+
23+
int QgsPointSample::createRandomPoints( QProgressDialog* pd )
24+
{
25+
Q_UNUSED( pd );
26+
27+
//create input layer from id (test if polygon, valid)
28+
if ( !mInputLayer )
29+
{
30+
return 1;
31+
}
32+
33+
if ( mInputLayer->geometryType() != QGis::Polygon )
34+
{
35+
return 2;
36+
}
37+
38+
//delete output file if it already exists
39+
if ( QFile::exists( mOutputLayer ) )
40+
{
41+
QgsVectorFileWriter::deleteShapeFile( mOutputLayer );
42+
}
43+
44+
//create vector file writer
45+
QgsFields outputFields;
46+
outputFields.append( QgsField( "id", QVariant::Int ) );
47+
outputFields.append( QgsField( "station_id", QVariant::Int ) );
48+
outputFields.append( QgsField( "stratum_id", QVariant::Int ) );
49+
QgsVectorFileWriter writer( mOutputLayer, "UTF-8",
50+
outputFields,
51+
QGis::WKBPoint,
52+
&( mInputLayer->crs() ) );
53+
54+
//check if creation of output layer successfull
55+
if ( writer.hasError() != QgsVectorFileWriter::NoError )
56+
{
57+
return 3;
58+
}
59+
60+
//init random number generator
61+
mt_srand( QTime::currentTime().msec() );
62+
QgsFeature fet;
63+
int nPoints = 0;
64+
double minDistance = 0;
65+
mNCreatedPoints = 0;
66+
67+
QgsFeatureIterator fIt = mInputLayer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes(
68+
QStringList() << mNumberOfPointsAttribute << mMinDistanceAttribute, mInputLayer->pendingFields() ) );
69+
while ( fIt.nextFeature( fet ) )
70+
{
71+
nPoints = fet.attribute( mNumberOfPointsAttribute ).toInt();
72+
if ( !mMinDistanceAttribute.isEmpty() )
73+
{
74+
minDistance = fet.attribute( mMinDistanceAttribute ).toDouble();
75+
}
76+
addSamplePoints( fet, writer, nPoints, minDistance );
77+
}
78+
79+
return 0;
80+
}
81+
82+
void QgsPointSample::addSamplePoints( QgsFeature& inputFeature, QgsVectorFileWriter& writer, int nPoints, double minDistance )
83+
{
84+
QgsGeometry* geom = inputFeature.geometry();
85+
if ( !geom )
86+
{
87+
return;
88+
}
89+
90+
QgsRectangle geomRect = geom->boundingBox();
91+
if ( geomRect.isEmpty() )
92+
{
93+
return;
94+
}
95+
96+
QgsSpatialIndex sIndex; //to check minimum distance
97+
QMap< QgsFeatureId, QgsPoint > pointMapForFeature;
98+
99+
int nIterations = 0;
100+
int maxIterations = nPoints * 200;
101+
int points = 0;
102+
103+
double randX = 0;
104+
double randY = 0;
105+
106+
while ( nIterations < maxIterations && points < nPoints )
107+
{
108+
randX = (( double )mt_rand() / RAND_MAX ) * geomRect.width() + geomRect.xMinimum();
109+
randY = (( double )mt_rand() / RAND_MAX ) * geomRect.height() + geomRect.yMinimum();
110+
QgsPoint randPoint( randX, randY );
111+
QgsGeometry* ptGeom = QgsGeometry::fromPoint( randPoint );
112+
if ( ptGeom->within( geom ) && checkMinDistance( randPoint, sIndex, minDistance, pointMapForFeature ) )
113+
{
114+
//add feature to writer
115+
QgsFeature f( mNCreatedPoints );
116+
f.setAttribute( "id", mNCreatedPoints + 1 );
117+
f.setAttribute( "station_id", points + 1 );
118+
f.setAttribute( "stratum_id", inputFeature.id() );
119+
f.setGeometry( ptGeom );
120+
writer.addFeature( f );
121+
sIndex.insertFeature( f );
122+
pointMapForFeature.insert( mNCreatedPoints, randPoint );
123+
++points;
124+
++mNCreatedPoints;
125+
}
126+
else
127+
{
128+
delete ptGeom;
129+
}
130+
++nIterations;
131+
}
132+
}
133+
134+
bool QgsPointSample::checkMinDistance( QgsPoint& pt, QgsSpatialIndex& index, double minDistance, QMap< QgsFeatureId, QgsPoint >& pointMap )
135+
{
136+
if ( minDistance <= 0 )
137+
{
138+
return true;
139+
}
140+
141+
QList<QgsFeatureId> neighborList = index.nearestNeighbor( pt, 1 );
142+
if ( neighborList.isEmpty() )
143+
{
144+
return true;
145+
}
146+
147+
QMap< QgsFeatureId, QgsPoint >::const_iterator it = pointMap.find( neighborList[0] );
148+
if ( it == pointMap.constEnd() ) //should not happen
149+
{
150+
return true;
151+
}
152+
153+
QgsPoint neighborPt = it.value();
154+
if ( neighborPt.sqrDist( pt ) < ( minDistance * minDistance ) )
155+
{
156+
return false;
157+
}
158+
return true;
159+
}
160+
161+
162+
163+

src/analysis/vector/qgspointsample.h

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#ifndef QGSPOINTSAMPLE_H
2+
#define QGSPOINTSAMPLE_H
3+
4+
#include "qgsfeature.h"
5+
#include <QString>
6+
7+
class QgsFeature;
8+
class QgsPoint;
9+
class QgsSpatialIndex;
10+
class QgsVectorFileWriter;
11+
class QgsVectorLayer;
12+
class QProgressDialog;
13+
14+
/**Creates random points in polygons / multipolygons*/
15+
class ANALYSIS_EXPORT QgsPointSample
16+
{
17+
public:
18+
QgsPointSample( QgsVectorLayer* inputLayer, const QString& outputLayer, QString nPointsAttribute, QString minDistAttribute = QString() );
19+
~QgsPointSample();
20+
21+
/**Starts calculation of random points
22+
@return 0 in case of success*/
23+
int createRandomPoints( QProgressDialog* pd );
24+
25+
private:
26+
27+
QgsPointSample(); //default constructor is forbidden
28+
void addSamplePoints( QgsFeature& inputFeature, QgsVectorFileWriter& writer, int nPoints, double minDistance );
29+
bool checkMinDistance( QgsPoint& pt, QgsSpatialIndex& index, double minDistance, QMap< QgsFeatureId, QgsPoint >& pointMap );
30+
31+
/**Layer id of input polygon/multipolygon layer*/
32+
QgsVectorLayer* mInputLayer;
33+
/**Output path of result layer*/
34+
QString mOutputLayer;
35+
/**Attribute containing number of points per feature*/
36+
QString mNumberOfPointsAttribute;
37+
/**Attribute containing minimum distance between sample points (or -1 if no min. distance constraint)*/
38+
QString mMinDistanceAttribute;
39+
QgsFeatureId mNCreatedPoints; //helper to find free ids
40+
};
41+
42+
#endif // QGSPOINTSAMPLE_H

src/analysis/vector/qgstransectsample.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ int QgsTransectSample::createSample( QProgressDialog* pd )
188188

189189
while ( nCreatedTransects < nTransects && nIterations < nMaxIterations )
190190
{
191-
double randomPosition = (( double )mt_rand() / std::numeric_limits<int32_t>::max() ) * clippedBaseline->length();
191+
double randomPosition = (( double )mt_rand() / RAND_MAX ) * clippedBaseline->length();
192192
QgsGeometry* samplePoint = clippedBaseline->interpolate( randomPosition );
193193
++nIterations;
194194
if ( !samplePoint )

0 commit comments

Comments
 (0)