Skip to content

Commit

Permalink
[FEATURE][processing] New algorithm "Repair Shapefile"
Browse files Browse the repository at this point in the history
Uses GDAL to repair shapefiles which have a broken or missing .SHX file
  • Loading branch information
nyalldawson committed Dec 4, 2019
1 parent b4bc7cb commit 7746061
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -106,6 +106,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmremoveholes.cpp
processing/qgsalgorithmremovenullgeometry.cpp
processing/qgsalgorithmrenamelayer.cpp
processing/qgsalgorithmrepairshapefile.cpp
processing/qgsalgorithmreverselinedirection.cpp
processing/qgsalgorithmrotate.cpp
processing/qgsalgorithmruggedness.cpp
Expand Down
96 changes: 96 additions & 0 deletions src/analysis/processing/qgsalgorithmrepairshapefile.cpp
@@ -0,0 +1,96 @@
/***************************************************************************
qgsalgorithmrepairshapefile.cpp
---------------------
begin : December 2019
copyright : (C) 2019 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsalgorithmrepairshapefile.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "cpl_conv.h"

///@cond PRIVATE

QString QgsRepairShapefileAlgorithm::name() const
{
return QStringLiteral( "repairshapefile" );
}

QString QgsRepairShapefileAlgorithm::displayName() const
{
return QObject::tr( "Repair Shapefile" );
}

QStringList QgsRepairShapefileAlgorithm::tags() const
{
return QObject::tr( "fix,shp,shx,broken,missing" ).split( ',' );
}

QString QgsRepairShapefileAlgorithm::group() const
{
return QObject::tr( "Vector general" );
}

QString QgsRepairShapefileAlgorithm::groupId() const
{
return QStringLiteral( "vectorgeneral" );
}

QString QgsRepairShapefileAlgorithm::shortHelpString() const
{
return QObject::tr( "Repairs a broken Shapefile by recreating missing or broken SHX files." );
}

QString QgsRepairShapefileAlgorithm::shortDescription() const
{
return QObject::tr( "Repairs broken Shapefiles by recreating SHX files." );
}

QgsRepairShapefileAlgorithm *QgsRepairShapefileAlgorithm::createInstance() const
{
return new QgsRepairShapefileAlgorithm();
}

void QgsRepairShapefileAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFile( QStringLiteral( "INPUT" ), QObject::tr( "Input Shapefile" ), QgsProcessingParameterFile::File,
QStringLiteral( "shp" ), QVariant(), false, QObject::tr( "ESRI Shapefile" ) +
QStringLiteral( " (*.shp *.SHP)" ) ) );

addOutput( new QgsProcessingOutputVectorLayer( QStringLiteral( "OUTPUT" ), QObject::tr( "Repaired layer" ) ) );
}

QVariantMap QgsRepairShapefileAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
const QString path = parameterAsFile( parameters, QStringLiteral( "INPUT" ), context );

if ( !QFile::exists( path ) )
throw QgsProcessingException( QObject::tr( "Could not load source layer for %1." ).arg( QStringLiteral( "INPUT" ) ) );

CPLSetConfigOption( "SHAPE_RESTORE_SHX", "YES" );

std::unique_ptr< QgsVectorLayer > layer = qgis::make_unique< QgsVectorLayer >( path );
if ( !layer->isValid() )
throw QgsProcessingException( QObject::tr( "Could not repair %1." ).arg( path ) );

CPLSetConfigOption( "SHAPE_RESTORE_SHX", nullptr );

feedback->pushInfo( QObject::tr( "Successfully repaired, found %1 features" ).arg( layer->featureCount() ) );

QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), path );
return outputs;
}

///@endcond
55 changes: 55 additions & 0 deletions src/analysis/processing/qgsalgorithmrepairshapefile.h
@@ -0,0 +1,55 @@
/***************************************************************************
qgsalgorithmrepairshapefile.h
---------------------
begin : December 2019
copyright : (C) 2019 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSALGORITHMREPAIRSHAPEFILE_H
#define QGSALGORITHMREPAIRSHAPEFILE_H

#define SIP_NO_FILE

#include "qgis_sip.h"
#include "qgsprocessingalgorithm.h"

///@cond PRIVATE

/**
* Native repair shapefile algorithm
*/
class QgsRepairShapefileAlgorithm : public QgsProcessingAlgorithm
{

public:

QgsRepairShapefileAlgorithm() = default;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
QString group() const override;
QString groupId() const override;
QString shortHelpString() const override;
QString shortDescription() const override;
QgsRepairShapefileAlgorithm *createInstance() const override SIP_FACTORY;

protected:

QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
};

///@endcond PRIVATE

#endif // QGSALGORITHMREPAIRSHAPEFILE_H
2 changes: 2 additions & 0 deletions src/analysis/processing/qgsnativealgorithms.cpp
Expand Up @@ -100,6 +100,7 @@
#include "qgsalgorithmremoveholes.h"
#include "qgsalgorithmremovenullgeometry.h"
#include "qgsalgorithmrenamelayer.h"
#include "qgsalgorithmrepairshapefile.h"
#include "qgsalgorithmreverselinedirection.h"
#include "qgsalgorithmrotate.h"
#include "qgsalgorithmruggedness.h"
Expand Down Expand Up @@ -277,6 +278,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsRemoveHolesAlgorithm() );
addAlgorithm( new QgsRemoveNullGeometryAlgorithm() );
addAlgorithm( new QgsRenameLayerAlgorithm() );
addAlgorithm( new QgsRepairShapefileAlgorithm() );
addAlgorithm( new QgsReverseLineDirectionAlgorithm() );
addAlgorithm( new QgsRotateFeaturesAlgorithm() );
addAlgorithm( new QgsRuggednessAlgorithm() );
Expand Down
41 changes: 40 additions & 1 deletion tests/src/analysis/testqgsprocessingalgs.cpp
Expand Up @@ -95,6 +95,8 @@ class TestQgsProcessingAlgs: public QObject
void bookmarksToLayer();
void layerToBookmarks();

void repairShapefile();

private:

QString mPointLayerPath;
Expand Down Expand Up @@ -516,7 +518,7 @@ void TestQgsProcessingAlgs::transformAlg()
parameters.insert( QStringLiteral( "TARGET_CRS" ), QStringLiteral( "EPSG:2163" ) );
bool ok = false;
QVariantMap results = alg->run( parameters, *context, &feedback, &ok );
Q_UNUSED( results );
Q_UNUSED( results )
QVERIFY( ok );
}

Expand Down Expand Up @@ -1615,5 +1617,42 @@ void TestQgsProcessingAlgs::layerToBookmarks()
QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 1 ).extent().toString( 0 ), QStringLiteral( "146,-22 : 147,-21" ) );
}

void TestQgsProcessingAlgs::repairShapefile()
{
QTemporaryDir tmpPath;

QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
QFile::copy( dataDir + "/points.shp", tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
QFile::copy( dataDir + "/points.shp", tmpPath.filePath( QStringLiteral( "points.prj" ) ) );
QFile::copy( dataDir + "/points.shp", tmpPath.filePath( QStringLiteral( "points.dbf" ) ) );
// no shx!!

std::unique_ptr< QgsVectorLayer > layer = qgis::make_unique< QgsVectorLayer >( tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
QVERIFY( !layer->isValid() );

std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:repairshapefile" ) ) );
QVERIFY( alg != nullptr );

QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "not a file" ) );

bool ok = false;
QgsProcessingFeedback feedback;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();

QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );

parameters.insert( QStringLiteral( "INPUT" ), tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "OUTPUT" ) ).toString(), tmpPath.filePath( QStringLiteral( "points.shp" ) ) );

layer = qgis::make_unique< QgsVectorLayer >( tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
QVERIFY( layer->isValid() );
}

QGSTEST_MAIN( TestQgsProcessingAlgs )
#include "testqgsprocessingalgs.moc"

0 comments on commit 7746061

Please sign in to comment.