Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsalgorithmnearestneighbouranalysis.cpp 3 : : --------------------- 4 : : begin : December 2019 5 : : copyright : (C) 2019 by Alexander Bruy 6 : : email : alexander dot bruy at gmail dot com 7 : : ***************************************************************************/ 8 : : 9 : : /*************************************************************************** 10 : : * * 11 : : * This program is free software; you can redistribute it and/or modify * 12 : : * it under the terms of the GNU General Public License as published by * 13 : : * the Free Software Foundation; either version 2 of the License, or * 14 : : * (at your option) any later version. * 15 : : * * 16 : : ***************************************************************************/ 17 : : 18 : : #include "qgsalgorithmnearestneighbouranalysis.h" 19 : : #include "qgsapplication.h" 20 : : #include <QTextStream> 21 : : 22 : : ///@cond PRIVATE 23 : : 24 : 0 : QString QgsNearestNeighbourAnalysisAlgorithm::name() const 25 : : { 26 : 0 : return QStringLiteral( "nearestneighbouranalysis" ); 27 : : } 28 : : 29 : 0 : QString QgsNearestNeighbourAnalysisAlgorithm::displayName() const 30 : : { 31 : 0 : return QObject::tr( "Nearest neighbour analysis" ); 32 : : } 33 : : 34 : 0 : QStringList QgsNearestNeighbourAnalysisAlgorithm::tags() const 35 : : { 36 : 0 : return QObject::tr( "point,node,vertex,nearest,neighbour,distance" ).split( ',' ); 37 : 0 : } 38 : : 39 : 0 : QString QgsNearestNeighbourAnalysisAlgorithm::group() const 40 : : { 41 : 0 : return QObject::tr( "Vector analysis" ); 42 : : } 43 : : 44 : 0 : QString QgsNearestNeighbourAnalysisAlgorithm::groupId() const 45 : : { 46 : 0 : return QStringLiteral( "vectoranalysis" ); 47 : : } 48 : : 49 : 0 : QString QgsNearestNeighbourAnalysisAlgorithm::shortHelpString() const 50 : : { 51 : 0 : return QObject::tr( "This algorithm performs nearest neighbor analysis for a point layer.\n\n" 52 : : "The output describes how the data are distributed (clustered, randomly or distributed).\n\n" 53 : : "Output is generated as an HTML file with the computed statistical values." ); 54 : : } 55 : : 56 : 0 : QString QgsNearestNeighbourAnalysisAlgorithm::svgIconPath() const 57 : : { 58 : 0 : return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmNearestNeighbour.svg" ) ); 59 : 0 : } 60 : : 61 : 0 : QIcon QgsNearestNeighbourAnalysisAlgorithm::icon() const 62 : : { 63 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmNearestNeighbour.svg" ) ); 64 : 0 : } 65 : : 66 : 0 : QgsNearestNeighbourAnalysisAlgorithm *QgsNearestNeighbourAnalysisAlgorithm::createInstance() const 67 : : { 68 : 0 : return new QgsNearestNeighbourAnalysisAlgorithm(); 69 : : } 70 : : 71 : 0 : void QgsNearestNeighbourAnalysisAlgorithm::initAlgorithm( const QVariantMap & ) 72 : : { 73 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList< int >() << QgsProcessing::TypeVectorPoint ) ); 74 : 0 : addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT_HTML_FILE" ), QObject::tr( "Nearest neighbour" ), 75 : 0 : QObject::tr( "HTML files (*.html *.HTML)" ), QVariant(), true ) ); 76 : 0 : addOutput( new QgsProcessingOutputNumber( QStringLiteral( "OBSERVED_MD" ), QObject::tr( "Observed mean distance" ) ) ); 77 : 0 : addOutput( new QgsProcessingOutputNumber( QStringLiteral( "EXPECTED_MD" ), QObject::tr( "Expected mean distance" ) ) ); 78 : 0 : addOutput( new QgsProcessingOutputNumber( QStringLiteral( "NN_INDEX" ), QObject::tr( "Nearest neighbour index" ) ) ); 79 : 0 : addOutput( new QgsProcessingOutputNumber( QStringLiteral( "POINT_COUNT" ), QObject::tr( "Number of points" ) ) ); 80 : 0 : addOutput( new QgsProcessingOutputNumber( QStringLiteral( "Z_SCORE" ), QObject::tr( "Z-score" ) ) ); 81 : 0 : } 82 : : 83 : 0 : QVariantMap QgsNearestNeighbourAnalysisAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) 84 : : { 85 : 0 : std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); 86 : 0 : if ( !source ) 87 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); 88 : : 89 : 0 : QString outputFile = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT_HTML_FILE" ), context ); 90 : : 91 : 0 : QgsSpatialIndex spatialIndex( *source, feedback, QgsSpatialIndex::FlagStoreFeatureGeometries ); 92 : 0 : QgsDistanceArea da; 93 : 0 : da.setSourceCrs( source->sourceCrs(), context.transformContext() ); 94 : 0 : da.setEllipsoid( context.ellipsoid() ); 95 : : 96 : 0 : double step = source->featureCount() ? 100.0 / source->featureCount() : 1; 97 : 0 : QgsFeatureIterator it = source->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QList< int >() ) ); 98 : : 99 : 0 : QgsFeatureRequest request; 100 : 0 : QgsFeature neighbour; 101 : 0 : double sumDist = 0.0; 102 : 0 : double area = source->sourceExtent().width() * source->sourceExtent().height(); 103 : : 104 : 0 : int i = 0; 105 : 0 : QgsFeature f; 106 : 0 : while ( it.nextFeature( f ) ) 107 : : { 108 : 0 : if ( feedback->isCanceled() ) 109 : : { 110 : 0 : break; 111 : : } 112 : : 113 : 0 : QgsFeatureId neighbourId = spatialIndex.nearestNeighbor( f.geometry().asPoint(), 2 ).at( 1 ); 114 : 0 : sumDist += da.measureLine( spatialIndex.geometry( neighbourId ).asPoint(), f.geometry().asPoint() ); 115 : : 116 : 0 : i++; 117 : 0 : feedback->setProgress( i * step ); 118 : : } 119 : : 120 : 0 : int count = source->featureCount() > 0 ? source->featureCount() : 1; 121 : 0 : double observedDistance = sumDist / count; 122 : 0 : double expectedDistance = 0.5 / std::sqrt( count / area ); 123 : 0 : double nnIndex = observedDistance / expectedDistance; 124 : 0 : double se = 0.26136 / std::sqrt( std::pow( count, 2 ) / area ); 125 : 0 : double zScore = ( observedDistance - expectedDistance ) / se; 126 : : 127 : 0 : QVariantMap outputs; 128 : 0 : outputs.insert( QStringLiteral( "OBSERVED_MD" ), observedDistance ); 129 : 0 : outputs.insert( QStringLiteral( "EXPECTED_MD" ), expectedDistance ); 130 : 0 : outputs.insert( QStringLiteral( "NN_INDEX" ), nnIndex ); 131 : 0 : outputs.insert( QStringLiteral( "POINT_COUNT" ), count ); 132 : 0 : outputs.insert( QStringLiteral( "Z_SCORE" ), zScore ); 133 : : 134 : 0 : if ( !outputFile.isEmpty() ) 135 : : { 136 : 0 : QFile file( outputFile ); 137 : 0 : if ( file.open( QIODevice::WriteOnly | QIODevice::Text ) ) 138 : : { 139 : 0 : QTextStream out( &file ); 140 : : #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 141 : 0 : out.setCodec( "UTF-8" ); 142 : : #endif 143 : 0 : out << QStringLiteral( "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/></head><body>\n" ); 144 : 0 : out << QObject::tr( "<p>Observed mean distance: %1</p>\n" ).arg( observedDistance, 0, 'f', 11 ); 145 : 0 : out << QObject::tr( "<p>Expected mean distance: %1</p>\n" ).arg( expectedDistance, 0, 'f', 11 ); 146 : 0 : out << QObject::tr( "<p>Nearest neighbour index: %1</p>\n" ).arg( nnIndex, 0, 'f', 11 ); 147 : 0 : out << QObject::tr( "<p>Number of points: %1</p>\n" ).arg( count ); 148 : 0 : out << QObject::tr( "<p>Z-Score: %1</p>\n" ).arg( zScore, 0, 'f', 11 ); 149 : 0 : out << QStringLiteral( "</body></html>" ); 150 : : 151 : 0 : outputs.insert( QStringLiteral( "OUTPUT_HTML_FILE" ), outputFile ); 152 : 0 : } 153 : 0 : } 154 : : 155 : 0 : return outputs; 156 : 0 : } 157 : : 158 : : ///@endcond