[FEATURE:] Add point sample class to analysis library

This commit is contained in:
Marco Hugentobler 2013-07-11 10:36:27 +02:00
parent b086195888
commit f351c4f7f8
8 changed files with 227 additions and 3 deletions

View File

@ -10,6 +10,7 @@
%Include vector/qgsgeometryanalyzer.sip
%Include vector/qgsoverlayanalyzer.sip
%Include vector/qgspointsample.sip
%Include vector/qgstransectsample.sip
%Include vector/qgszonalstatistics.sip

View File

@ -0,0 +1,17 @@
/** \ingroup analysis
*/
class QgsPointSample
{
%TypeHeaderCode
#include <qgspointsample.h>
%End
public:
QgsPointSample( QgsVectorLayer* inputLayer, const QString& outputLayer, QString nPointsAttribute, QString minDistAttribute = QString() );
~QgsPointSample();
/**Starts calculation of random points
@return 0 in case of success*/
int createRandomPoints( QProgressDialog* pd );
};

View File

@ -36,6 +36,7 @@ SET(QGIS_ANALYSIS_SRCS
raster/qgsrastermatrix.cpp
vector/mersenne-twister.cpp
vector/qgsgeometryanalyzer.cpp
vector/qgspointsample.cpp
vector/qgstransectsample.cpp
vector/qgszonalstatistics.cpp
vector/qgsoverlayanalyzer.cpp

View File

@ -22,7 +22,6 @@
#include <stdio.h>
#include <stdint.h>
#include <limits>
#include "mersenne-twister.h"
/*

View File

@ -23,6 +23,7 @@
#define MERSENNE_TWISTER_H
#include <stdint.h>
#include <limits>
#ifdef __cplusplus
extern "C" {
@ -31,7 +32,7 @@ extern "C" {
/*
* Maximum number you can get from rand().
*/
#define RAND_MAX INT32_MAX
#define RAND_MAX std::numeric_limits<int32_t>::max()
/*
* Initialize the number generator with given seed.

View File

@ -0,0 +1,163 @@
#include "qgspointsample.h"
#include "qgsgeometry.h"
#include "qgsspatialindex.h"
#include "qgsvectorfilewriter.h"
#include "qgsvectorlayer.h"
#include <QFile>
#include "mersenne-twister.h"
QgsPointSample::QgsPointSample( QgsVectorLayer* inputLayer, const QString& outputLayer, QString nPointsAttribute, QString minDistAttribute ): mInputLayer( inputLayer ),
mOutputLayer( outputLayer ), mNumberOfPointsAttribute( nPointsAttribute ), mMinDistanceAttribute( minDistAttribute ), mNCreatedPoints( 0 )
{
}
QgsPointSample::QgsPointSample()
{
}
QgsPointSample::~QgsPointSample()
{
}
int QgsPointSample::createRandomPoints( QProgressDialog* pd )
{
Q_UNUSED( pd );
//create input layer from id (test if polygon, valid)
if ( !mInputLayer )
{
return 1;
}
if ( mInputLayer->geometryType() != QGis::Polygon )
{
return 2;
}
//delete output file if it already exists
if ( QFile::exists( mOutputLayer ) )
{
QgsVectorFileWriter::deleteShapeFile( mOutputLayer );
}
//create vector file writer
QgsFields outputFields;
outputFields.append( QgsField( "id", QVariant::Int ) );
outputFields.append( QgsField( "station_id", QVariant::Int ) );
outputFields.append( QgsField( "stratum_id", QVariant::Int ) );
QgsVectorFileWriter writer( mOutputLayer, "UTF-8",
outputFields,
QGis::WKBPoint,
&( mInputLayer->crs() ) );
//check if creation of output layer successfull
if ( writer.hasError() != QgsVectorFileWriter::NoError )
{
return 3;
}
//init random number generator
mt_srand( QTime::currentTime().msec() );
QgsFeature fet;
int nPoints = 0;
double minDistance = 0;
mNCreatedPoints = 0;
QgsFeatureIterator fIt = mInputLayer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes(
QStringList() << mNumberOfPointsAttribute << mMinDistanceAttribute, mInputLayer->pendingFields() ) );
while ( fIt.nextFeature( fet ) )
{
nPoints = fet.attribute( mNumberOfPointsAttribute ).toInt();
if ( !mMinDistanceAttribute.isEmpty() )
{
minDistance = fet.attribute( mMinDistanceAttribute ).toDouble();
}
addSamplePoints( fet, writer, nPoints, minDistance );
}
return 0;
}
void QgsPointSample::addSamplePoints( QgsFeature& inputFeature, QgsVectorFileWriter& writer, int nPoints, double minDistance )
{
QgsGeometry* geom = inputFeature.geometry();
if ( !geom )
{
return;
}
QgsRectangle geomRect = geom->boundingBox();
if ( geomRect.isEmpty() )
{
return;
}
QgsSpatialIndex sIndex; //to check minimum distance
QMap< QgsFeatureId, QgsPoint > pointMapForFeature;
int nIterations = 0;
int maxIterations = nPoints * 200;
int points = 0;
double randX = 0;
double randY = 0;
while ( nIterations < maxIterations && points < nPoints )
{
randX = (( double )mt_rand() / RAND_MAX ) * geomRect.width() + geomRect.xMinimum();
randY = (( double )mt_rand() / RAND_MAX ) * geomRect.height() + geomRect.yMinimum();
QgsPoint randPoint( randX, randY );
QgsGeometry* ptGeom = QgsGeometry::fromPoint( randPoint );
if ( ptGeom->within( geom ) && checkMinDistance( randPoint, sIndex, minDistance, pointMapForFeature ) )
{
//add feature to writer
QgsFeature f( mNCreatedPoints );
f.setAttribute( "id", mNCreatedPoints + 1 );
f.setAttribute( "station_id", points + 1 );
f.setAttribute( "stratum_id", inputFeature.id() );
f.setGeometry( ptGeom );
writer.addFeature( f );
sIndex.insertFeature( f );
pointMapForFeature.insert( mNCreatedPoints, randPoint );
++points;
++mNCreatedPoints;
}
else
{
delete ptGeom;
}
++nIterations;
}
}
bool QgsPointSample::checkMinDistance( QgsPoint& pt, QgsSpatialIndex& index, double minDistance, QMap< QgsFeatureId, QgsPoint >& pointMap )
{
if ( minDistance <= 0 )
{
return true;
}
QList<QgsFeatureId> neighborList = index.nearestNeighbor( pt, 1 );
if ( neighborList.isEmpty() )
{
return true;
}
QMap< QgsFeatureId, QgsPoint >::const_iterator it = pointMap.find( neighborList[0] );
if ( it == pointMap.constEnd() ) //should not happen
{
return true;
}
QgsPoint neighborPt = it.value();
if ( neighborPt.sqrDist( pt ) < ( minDistance * minDistance ) )
{
return false;
}
return true;
}

View File

@ -0,0 +1,42 @@
#ifndef QGSPOINTSAMPLE_H
#define QGSPOINTSAMPLE_H
#include "qgsfeature.h"
#include <QString>
class QgsFeature;
class QgsPoint;
class QgsSpatialIndex;
class QgsVectorFileWriter;
class QgsVectorLayer;
class QProgressDialog;
/**Creates random points in polygons / multipolygons*/
class ANALYSIS_EXPORT QgsPointSample
{
public:
QgsPointSample( QgsVectorLayer* inputLayer, const QString& outputLayer, QString nPointsAttribute, QString minDistAttribute = QString() );
~QgsPointSample();
/**Starts calculation of random points
@return 0 in case of success*/
int createRandomPoints( QProgressDialog* pd );
private:
QgsPointSample(); //default constructor is forbidden
void addSamplePoints( QgsFeature& inputFeature, QgsVectorFileWriter& writer, int nPoints, double minDistance );
bool checkMinDistance( QgsPoint& pt, QgsSpatialIndex& index, double minDistance, QMap< QgsFeatureId, QgsPoint >& pointMap );
/**Layer id of input polygon/multipolygon layer*/
QgsVectorLayer* mInputLayer;
/**Output path of result layer*/
QString mOutputLayer;
/**Attribute containing number of points per feature*/
QString mNumberOfPointsAttribute;
/**Attribute containing minimum distance between sample points (or -1 if no min. distance constraint)*/
QString mMinDistanceAttribute;
QgsFeatureId mNCreatedPoints; //helper to find free ids
};
#endif // QGSPOINTSAMPLE_H

View File

@ -188,7 +188,7 @@ int QgsTransectSample::createSample( QProgressDialog* pd )
while ( nCreatedTransects < nTransects && nIterations < nMaxIterations )
{
double randomPosition = (( double )mt_rand() / std::numeric_limits<int32_t>::max() ) * clippedBaseline->length();
double randomPosition = (( double )mt_rand() / RAND_MAX ) * clippedBaseline->length();
QgsGeometry* samplePoint = clippedBaseline->interpolate( randomPosition );
++nIterations;
if ( !samplePoint )