From 0227bdc3762258a1ac5bcf4ff2a5b76dfd7705bb Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 7 Aug 2017 06:35:01 +1000 Subject: [PATCH 01/19] Remove QgsGeometryAnalyzer, QgsOverlayAnalyzer, QgsPointSample These classes are unused in the master QGIS code, and are unmaintained and with no unit tests or other QA, and have inflexible API (e.g. always requiring writing outputs to shapefiles) They all have equivalent algorithms available via Processing (where the algorithms are unit tested and maintained). We should be pushing all QGIS api users to use the Processing algorithms instead. --- doc/api_break.dox | 2 + python/analysis/analysis_auto.sip | 3 - .../analysis/vector/qgsgeometryanalyzer.sip | 143 -- python/analysis/vector/qgsoverlayanalyzer.sip | 46 - python/analysis/vector/qgspointsample.sip | 38 - src/analysis/CMakeLists.txt | 6 - src/analysis/vector/qgsgeometryanalyzer.cpp | 1493 ----------------- src/analysis/vector/qgsgeometryanalyzer.h | 161 -- src/analysis/vector/qgsoverlayanalyzer.cpp | 195 --- src/analysis/vector/qgsoverlayanalyzer.h | 54 - src/analysis/vector/qgspointsample.cpp | 170 -- src/analysis/vector/qgspointsample.h | 57 - tests/src/analysis/CMakeLists.txt | 1 - tests/src/analysis/testqgsvectoranalyzer.cpp | 145 -- 14 files changed, 2 insertions(+), 2512 deletions(-) delete mode 100644 python/analysis/vector/qgsgeometryanalyzer.sip delete mode 100644 python/analysis/vector/qgsoverlayanalyzer.sip delete mode 100644 python/analysis/vector/qgspointsample.sip delete mode 100644 src/analysis/vector/qgsgeometryanalyzer.cpp delete mode 100644 src/analysis/vector/qgsgeometryanalyzer.h delete mode 100644 src/analysis/vector/qgsoverlayanalyzer.cpp delete mode 100644 src/analysis/vector/qgsoverlayanalyzer.h delete mode 100644 src/analysis/vector/qgspointsample.cpp delete mode 100644 src/analysis/vector/qgspointsample.h delete mode 100644 tests/src/analysis/testqgsvectoranalyzer.cpp diff --git a/doc/api_break.dox b/doc/api_break.dox index 1ff0c69d92e..4f3a969fddc 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -269,6 +269,7 @@ should now call QgsCoordinateReferenceSystem::invalidateCache() and QgsCoordinat - QgsFileNameWidgetWrapper was removed. Use QgsExternalResourceWidgetWrapper instead. - QgsFileDropEdit was removed. Use QgsFileWidget instead. - QgsFormAnnotationItem. Use QgsFormAnnotation instead. +- QgsGeometryAnalyzer. Use the equivalent Processing algorithms instead. - QgsHtmlAnnotationItem. Use QgsHtmlAnnotation instead. - QgsHttpTransaction. This class was outdated and code should be ported to native Qt or Python implementations. - QgsGenericProjectionSelector. Use QgsProjectionSelectionTreeWidget instead. @@ -287,6 +288,7 @@ should now call QgsCoordinateReferenceSystem::invalidateCache() and QgsCoordinat - QgsMapRenderer. It has been replaced by QgsMapRendererJob with subclasses and QgsMapSettings. - QgsMapToolTouch. The touch navigation functionality is now built into the standard QgsMapToolPan tool. - QgsPhotoWidgetWrapper was removed. Use QgsExternalResourceWidgetWrapper instead. +- QgsPointSample. Use the Processing "Random Points in Polygon" algorithm instead. - QgsPseudoColorShader. This shader has been broken for some time and was replaced by QgsSingleBandPseudoColorRenderer. - QgsProjectBadLayerGuiHandler was removed. It was unused in QGIS code and barely useful. Implement your own QgsProjectBadLayerHandler subclass if needed. - QgsRendererV2DataDefinedMenus was removed. Use QgsPropertyOverrideButton instead. diff --git a/python/analysis/analysis_auto.sip b/python/analysis/analysis_auto.sip index 898598b2041..4966059b096 100644 --- a/python/analysis/analysis_auto.sip +++ b/python/analysis/analysis_auto.sip @@ -12,9 +12,6 @@ %Include raster/qgsrastermatrix.sip %Include raster/qgsrastercalcnode.sip %Include raster/qgstotalcurvaturefilter.sip -%Include vector/qgsgeometryanalyzer.sip -%Include vector/qgsoverlayanalyzer.sip -%Include vector/qgspointsample.sip %Include vector/qgstransectsample.sip %Include vector/qgszonalstatistics.sip %Include interpolation/qgsinterpolator.sip diff --git a/python/analysis/vector/qgsgeometryanalyzer.sip b/python/analysis/vector/qgsgeometryanalyzer.sip deleted file mode 100644 index 47f4a4a493e..00000000000 --- a/python/analysis/vector/qgsgeometryanalyzer.sip +++ /dev/null @@ -1,143 +0,0 @@ -/************************************************************************ - * This file has been generated automatically from * - * * - * src/analysis/vector/qgsgeometryanalyzer.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ - - - - - - -class QgsGeometryAnalyzer -{ -%Docstring - The QGis class provides vector geometry analysis functions -%End - -%TypeHeaderCode -#include "qgsgeometryanalyzer.h" -%End - public: - - bool simplify( QgsVectorLayer *layer, const QString &shapefileName, double tolerance, - bool onlySelectedFeatures = false, QProgressDialog *p = 0 ); -%Docstring - Simplify vector layer using (a modified) Douglas-Peucker algorithm - and write it to a new shape file - \param layer input vector layer - \param shapefileName path to the output shp - \param tolerance (level of simplification) - \param onlySelectedFeatures if true, only selected features are considered, else all the features - \param p progress dialog (or 0 if no progress dialog is to be shown) - :rtype: bool -%End - - bool centroids( QgsVectorLayer *layer, const QString &shapefileName, - bool onlySelectedFeatures = false, QProgressDialog *p = 0 ); -%Docstring - Calculate the true centroids, or 'center of mass' for a vector layer and - write it to a new shape file - \param layer input vector layer - \param shapefileName path to the output shp - \param onlySelectedFeatures if true, only selected features are considered, else all the features - \param p progress dialog (or 0 if no progress dialog is to be shown) - :rtype: bool -%End - - bool extent( QgsVectorLayer *layer, const QString &shapefileName, bool onlySelectedFeatures = false, QProgressDialog *p = 0 ); -%Docstring - Create a polygon based on the extent of all (selected) features and write it to a new shape file - \param layer input vector layer - \param shapefileName path to the output shp - \param onlySelectedFeatures if true, only selected features are considered, else all the features - \param p progress dialog (or 0 if no progress dialog is to be shown) - :rtype: bool -%End - - bool buffer( QgsVectorLayer *layer, const QString &shapefileName, double bufferDistance, - bool onlySelectedFeatures = false, bool dissolve = false, int bufferDistanceField = -1, QProgressDialog *p = 0 ); -%Docstring - Create buffers for a vector layer and write it to a new shape file - \param layer input vector layer - \param shapefileName path to the output shp - \param bufferDistance distance for buffering (if no buffer field is specified) - \param onlySelectedFeatures if true, only selected features are considered, else all the features - \param dissolve if true, merge all the buffers to a big multipolygon - \param bufferDistanceField index of the attribute field that contains the buffer distance (or -1 if all features have the same buffer distance) - \param p progress dialog (or 0 if no progress dialog is to be shown) - :rtype: bool -%End - - bool convexHull( QgsVectorLayer *layer, const QString &shapefileName, bool onlySelectedFeatures = false, - int uniqueIdField = -1, QProgressDialog *p = 0 ); -%Docstring - Create convex hull(s) of a vector layer and write it to a new shape file - \param layer input vector layer - \param shapefileName path to the output shp - \param onlySelectedFeatures if true, only selected features are considered, else all the features - \param uniqueIdField index of the attribute field that contains the unique convex hull id (or -1 if - all features have the same buffer distance) - \param p progress dialog (or 0 if no progress dialog is to be shown) - :rtype: bool -%End - - bool dissolve( QgsVectorLayer *layer, const QString &shapefileName, bool onlySelectedFeatures = false, - int uniqueIdField = -1, QProgressDialog *p = 0 ); -%Docstring - Dissolve a vector layer and write it to a new shape file - \param layer input vector layer - \param shapefileName path to the output shp - \param onlySelectedFeatures if true, only selected features are considered, else all the features - \param uniqueIdField index of the attribute field that contains the unique id to dissolve on (or -1 if - all features should be dissolved together) - \param p progress dialog (or 0 if no progress dialog is to be shown) - :rtype: bool -%End - - bool eventLayer( QgsVectorLayer *lineLayer, QgsVectorLayer *eventLayer, int lineField, int eventField, QgsFeatureIds &unlocatedFeatureIds /Out/, const QString &outputLayer, - const QString &outputFormat, int locationField1, int locationField2 = -1, int offsetField = -1, double offsetScale = 1.0, - bool forceSingleGeometry = false, QgsVectorDataProvider *memoryProvider = 0, QProgressDialog *p = 0 ); -%Docstring - Creates an event layer (multipoint or multiline) by locating features from a (non-spatial) event table along the features of a line layer. - Note that currently (until QgsGeometry supports m-values) the z-coordinate of the line layer is used for linear referencing - \param lineLayer layer with the line geometry - \param eventLayer layer with features and location field - \param lineField join index in line layer - \param eventField join index in event layer - \param outputLayer name of output file (can be empty if a memory layer is used) - \param outputFormat name of output format (can be empty if a memory provider is used to store the results) - \param unlocatedFeatureIds out: ids of event features where linear referencing was not successful - \param locationField1 attribute index of location field in event layer - \param locationField2 attribute index of location end field (or -1 for point layer) - \param offsetField attribute index for offset field. Negative offset value = offset to left side, positive value = offset to right side - \param offsetScale factor to scale offset - \param forceSingleGeometry force layer to single point/line type. Feature attributes are copied in case of multiple matches - \param memoryProvider memory provider to write output to (can be 0 if output is written to a file) - \param p progress dialog or 0 if no progress dialog should be shown - :rtype: bool -%End - - QgsGeometry locateBetweenMeasures( double fromMeasure, double toMeasure, const QgsGeometry &lineGeom ); -%Docstring -Returns linear reference geometry as a multiline (or 0 if no match). Currently, the z-coordinates are considered to be the measures (no support for m-values in QGIS) - :rtype: QgsGeometry -%End - - QgsGeometry locateAlongMeasure( double measure, const QgsGeometry &lineGeom ); -%Docstring - Returns linear reference geometry. Unlike the PostGIS function, this method always returns multipoint or 0 if no match (not geometry collection). - Currently, the z-coordinates are considered to be the measures (no support for m-values in QGIS) - :rtype: QgsGeometry -%End - -}; -/************************************************************************ - * This file has been generated automatically from * - * * - * src/analysis/vector/qgsgeometryanalyzer.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ diff --git a/python/analysis/vector/qgsoverlayanalyzer.sip b/python/analysis/vector/qgsoverlayanalyzer.sip deleted file mode 100644 index fd550e1bca7..00000000000 --- a/python/analysis/vector/qgsoverlayanalyzer.sip +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************************ - * This file has been generated automatically from * - * * - * src/analysis/vector/qgsoverlayanalyzer.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ - - - - - - -class QgsOverlayAnalyzer -{ -%Docstring - The QGis class provides vector overlay analysis functions -%End - -%TypeHeaderCode -#include "qgsoverlayanalyzer.h" -%End - public: - - bool intersection( QgsVectorLayer *layerA, QgsVectorLayer *layerB, - const QString &shapefileName, bool onlySelectedFeatures = false, - QProgressDialog *p = 0 ); -%Docstring - Perform an intersection on two input vector layers and write output to a new shape file -\param layerA input vector layer -\param layerB input vector layer -\param shapefileName path to the output shp -\param onlySelectedFeatures if true, only selected features are considered, else all the features -\param p progress dialog (or 0 if no progress dialog is to be shown) - :rtype: bool -%End - -}; - -/************************************************************************ - * This file has been generated automatically from * - * * - * src/analysis/vector/qgsoverlayanalyzer.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ diff --git a/python/analysis/vector/qgspointsample.sip b/python/analysis/vector/qgspointsample.sip deleted file mode 100644 index f685510b82b..00000000000 --- a/python/analysis/vector/qgspointsample.sip +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************************************ - * This file has been generated automatically from * - * * - * src/analysis/vector/qgspointsample.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ - - - -class QgsPointSample -{ -%Docstring - Creates random points in polygons / multipolygons* -%End - -%TypeHeaderCode -#include "qgspointsample.h" -%End - public: - QgsPointSample( QgsVectorLayer *inputLayer, const QString &outputLayer, const QString &nPointsAttribute, const QString &minDistAttribute = QString() ); - - int createRandomPoints( QProgressDialog *pd ); -%Docstring - Starts calculation of random points -:return: 0 in case of success* - :rtype: int -%End - -}; - -/************************************************************************ - * This file has been generated automatically from * - * * - * src/analysis/vector/qgspointsample.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index 6aa593fbae8..072f6f3dd3c 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -35,12 +35,9 @@ SET(QGIS_ANALYSIS_SRCS raster/qgsrastercalculator.cpp raster/qgsrastermatrix.cpp vector/mersenne-twister.cpp - vector/qgsgeometryanalyzer.cpp vector/qgsgeometrysnapper.cpp - vector/qgspointsample.cpp vector/qgstransectsample.cpp vector/qgszonalstatistics.cpp - vector/qgsoverlayanalyzer.cpp openstreetmap/qgsosmbase.cpp openstreetmap/qgsosmdatabase.cpp @@ -116,9 +113,6 @@ SET(QGIS_ANALYSIS_HDRS raster/qgsrastercalcnode.h raster/qgstotalcurvaturefilter.h - vector/qgsgeometryanalyzer.h - vector/qgsoverlayanalyzer.h - vector/qgspointsample.h vector/qgstransectsample.h vector/qgszonalstatistics.h diff --git a/src/analysis/vector/qgsgeometryanalyzer.cpp b/src/analysis/vector/qgsgeometryanalyzer.cpp deleted file mode 100644 index ddfcb087ef9..00000000000 --- a/src/analysis/vector/qgsgeometryanalyzer.cpp +++ /dev/null @@ -1,1493 +0,0 @@ -/*************************************************************************** - qgsgeometryanalyzer.cpp - QGIS Tools for vector geometry analysis - ------------------- - begin : 19 March 2009 - copyright : (C) Carson Farmer - email : carson.farmer@gmail.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 "qgsgeometryanalyzer.h" - -#include "qgsapplication.h" -#include "qgsfields.h" -#include "qgsfeature.h" -#include "qgsfeatureiterator.h" -#include "qgslogger.h" -#include "qgscoordinatereferencesystem.h" -#include "qgsvectorfilewriter.h" -#include "qgsvectordataprovider.h" -#include "qgsdistancearea.h" -#include "qgis.h" -#include "qgsvectorlayer.h" - -#include - -bool QgsGeometryAnalyzer::simplify( QgsVectorLayer *layer, - const QString &shapefileName, - double tolerance, - bool onlySelectedFeatures, - QProgressDialog *p ) -{ - if ( !layer ) - { - return false; - } - - QgsVectorDataProvider *dp = layer->dataProvider(); - if ( !dp ) - { - return false; - } - - QgsWkbTypes::Type outputType = dp->wkbType(); - QgsCoordinateReferenceSystem crs = layer->crs(); - - QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, crs ); - QgsFeature currentFeature; - - //take only selection - if ( onlySelectedFeatures ) - { - //use QgsVectorLayer::featureAtId - const QgsFeatureIds selection = layer->selectedFeatureIds(); - if ( p ) - { - p->setMaximum( selection.size() ); - } - - int processedFeatures = 0; - QgsFeatureIds::const_iterator it = selection.constBegin(); - for ( ; it != selection.constEnd(); ++it ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - - if ( p && p->wasCanceled() ) - { - break; - } - if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) ) - { - continue; - } - simplifyFeature( currentFeature, &vWriter, tolerance ); - ++processedFeatures; - } - - if ( p ) - { - p->setValue( selection.size() ); - } - } - //take all features - else - { - QgsFeatureIterator fit = layer->getFeatures(); - - int featureCount = layer->featureCount(); - if ( p ) - { - p->setMaximum( featureCount ); - } - int processedFeatures = 0; - - while ( fit.nextFeature( currentFeature ) ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - if ( p && p->wasCanceled() ) - { - break; - } - simplifyFeature( currentFeature, &vWriter, tolerance ); - ++processedFeatures; - } - if ( p ) - { - p->setValue( featureCount ); - } - } - - return true; -} - -void QgsGeometryAnalyzer::simplifyFeature( QgsFeature &f, QgsVectorFileWriter *vfw, double tolerance ) -{ - if ( !f.hasGeometry() ) - { - return; - } - - QgsGeometry featureGeometry = f.geometry(); - - // simplify feature - QgsGeometry tmpGeometry = featureGeometry.simplify( tolerance ); - - QgsFeature newFeature; - newFeature.setGeometry( tmpGeometry ); - newFeature.setAttributes( f.attributes() ); - - //add it to vector file writer - if ( vfw ) - { - vfw->addFeature( newFeature ); - } -} - -bool QgsGeometryAnalyzer::centroids( QgsVectorLayer *layer, const QString &shapefileName, - bool onlySelectedFeatures, QProgressDialog *p ) -{ - if ( !layer ) - { - QgsDebugMsg( "No layer passed to centroids" ); - return false; - } - - QgsVectorDataProvider *dp = layer->dataProvider(); - if ( !dp ) - { - QgsDebugMsg( "No data provider for layer passed to centroids" ); - return false; - } - - QgsWkbTypes::Type outputType = QgsWkbTypes::Point; - QgsCoordinateReferenceSystem crs = layer->crs(); - - QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, crs ); - QgsFeature currentFeature; - - //take only selection - if ( onlySelectedFeatures ) - { - //use QgsVectorLayer::featureAtId - const QgsFeatureIds selection = layer->selectedFeatureIds(); - if ( p ) - { - p->setMaximum( selection.size() ); - } - - int processedFeatures = 0; - QgsFeatureIds::const_iterator it = selection.constBegin(); - for ( ; it != selection.constEnd(); ++it ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - - if ( p && p->wasCanceled() ) - { - break; - } - if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) ) - { - continue; - } - centroidFeature( currentFeature, &vWriter ); - ++processedFeatures; - } - - if ( p ) - { - p->setValue( selection.size() ); - } - } - //take all features - else - { - QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) ); - - int featureCount = layer->featureCount(); - if ( p ) - { - p->setMaximum( featureCount ); - } - int processedFeatures = 0; - - while ( fit.nextFeature( currentFeature ) ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - if ( p && p->wasCanceled() ) - { - break; - } - centroidFeature( currentFeature, &vWriter ); - ++processedFeatures; - } - if ( p ) - { - p->setValue( featureCount ); - } - } - - return true; -} - - -void QgsGeometryAnalyzer::centroidFeature( QgsFeature &f, QgsVectorFileWriter *vfw ) -{ - if ( !f.hasGeometry() ) - { - return; - } - - QgsGeometry featureGeometry = f.geometry(); - QgsFeature newFeature; - newFeature.setGeometry( featureGeometry.centroid() ); - newFeature.setAttributes( f.attributes() ); - - //add it to vector file writer - if ( vfw ) - { - vfw->addFeature( newFeature ); - } -} - -bool QgsGeometryAnalyzer::extent( QgsVectorLayer *layer, - const QString &shapefileName, - bool onlySelectedFeatures, - QProgressDialog * ) -{ - if ( !layer ) - { - return false; - } - - QgsVectorDataProvider *dp = layer->dataProvider(); - if ( !dp ) - { - return false; - } - - QgsWkbTypes::Type outputType = QgsWkbTypes::Polygon; - QgsCoordinateReferenceSystem crs = layer->crs(); - - QgsFields fields; - fields.append( QgsField( QStringLiteral( "MINX" ), QVariant::Double ) ); - fields.append( QgsField( QStringLiteral( "MINY" ), QVariant::Double ) ); - fields.append( QgsField( QStringLiteral( "MAXX" ), QVariant::Double ) ); - fields.append( QgsField( QStringLiteral( "MAXY" ), QVariant::Double ) ); - fields.append( QgsField( QStringLiteral( "CNTX" ), QVariant::Double ) ); - fields.append( QgsField( QStringLiteral( "CNTY" ), QVariant::Double ) ); - fields.append( QgsField( QStringLiteral( "AREA" ), QVariant::Double ) ); - fields.append( QgsField( QStringLiteral( "PERIM" ), QVariant::Double ) ); - fields.append( QgsField( QStringLiteral( "HEIGHT" ), QVariant::Double ) ); - fields.append( QgsField( QStringLiteral( "WIDTH" ), QVariant::Double ) ); - - QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, crs ); - - QgsRectangle rect; - if ( onlySelectedFeatures ) // take only selection - { - rect = layer->boundingBoxOfSelected(); - } - else - { - rect = layer->extent(); - } - - double minx = rect.xMinimum(); - double miny = rect.yMinimum(); - double maxx = rect.xMaximum(); - double maxy = rect.yMaximum(); - double height = rect.height(); - double width = rect.width(); - double cntx = minx + ( width / 2.0 ); - double cnty = miny + ( height / 2.0 ); - double area = width * height; - double perim = ( 2 * width ) + ( 2 * height ); - - QgsFeature feat; - QgsAttributes attrs( 10 ); - attrs[0] = QVariant( minx ); - attrs[1] = QVariant( miny ); - attrs[2] = QVariant( maxx ); - attrs[3] = QVariant( maxy ); - attrs[4] = QVariant( cntx ); - attrs[5] = QVariant( cnty ); - attrs[6] = QVariant( area ); - attrs[7] = QVariant( perim ); - attrs[8] = QVariant( height ); - attrs[9] = QVariant( width ); - feat.setAttributes( attrs ); - feat.setGeometry( QgsGeometry::fromRect( rect ) ); - vWriter.addFeature( feat ); - return true; -} - -QList QgsGeometryAnalyzer::simpleMeasure( QgsGeometry &mpGeometry ) -{ - QList list; - double perim; - if ( mpGeometry.wkbType() == QgsWkbTypes::Point ) - { - QgsPointXY pt = mpGeometry.asPoint(); - list.append( pt.x() ); - list.append( pt.y() ); - } - else - { - QgsDistanceArea measure; - list.append( measure.measureArea( mpGeometry ) ); - if ( mpGeometry.type() == QgsWkbTypes::PolygonGeometry ) - { - perim = perimeterMeasure( mpGeometry, measure ); - list.append( perim ); - } - } - return list; -} - -double QgsGeometryAnalyzer::perimeterMeasure( const QgsGeometry &geometry, QgsDistanceArea &measure ) -{ - return measure.measurePerimeter( geometry ); -} - -bool QgsGeometryAnalyzer::convexHull( QgsVectorLayer *layer, const QString &shapefileName, - bool onlySelectedFeatures, int uniqueIdField, QProgressDialog *p ) -{ - if ( !layer ) - { - return false; - } - QgsVectorDataProvider *dp = layer->dataProvider(); - if ( !dp ) - { - return false; - } - bool useField = false; - if ( uniqueIdField == -1 ) - { - uniqueIdField = 0; - } - else - { - useField = true; - } - QgsFields fields; - fields.append( QgsField( QStringLiteral( "UID" ), QVariant::String ) ); - fields.append( QgsField( QStringLiteral( "AREA" ), QVariant::Double ) ); - fields.append( QgsField( QStringLiteral( "PERIM" ), QVariant::Double ) ); - - QgsWkbTypes::Type outputType = QgsWkbTypes::Polygon; - QgsCoordinateReferenceSystem crs = layer->crs(); - - QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, crs ); - QgsFeature currentFeature; - QgsGeometry dissolveGeometry; //dissolve geometry - QMultiMap map; - - if ( onlySelectedFeatures ) - { - //use QgsVectorLayer::featureAtId - const QgsFeatureIds selection = layer->selectedFeatureIds(); - QgsFeatureIds::const_iterator it = selection.constBegin(); - for ( ; it != selection.constEnd(); ++it ) - { -#if 0 - if ( p ) - { - p->setValue( processedFeatures ); - } - if ( p && p->wasCanceled() ) - { - // break; // it may be better to do something else here? - return false; - } -#endif - if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) ) - { - continue; - } - map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); - } - } - else - { - QgsFeatureIterator fit = layer->getFeatures(); - while ( fit.nextFeature( currentFeature ) ) - { -#if 0 - if ( p ) - { - p->setValue( processedFeatures ); - } - if ( p && p->wasCanceled() ) - { - // break; // it may be better to do something else here? - return false; - } -#endif - map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); - } - } - - QMultiMap::const_iterator jt = map.constBegin(); - while ( jt != map.constEnd() ) - { - QString currentKey = jt.key(); - int processedFeatures = 0; - //take only selection - if ( onlySelectedFeatures ) - { - //use QgsVectorLayer::featureAtId - const QgsFeatureIds selection = layer->selectedFeatureIds(); - if ( p ) - { - p->setMaximum( selection.size() ); - } - processedFeatures = 0; - while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) - { - if ( p && p->wasCanceled() ) - { - break; - } - if ( selection.contains( jt.value() ) ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) - { - continue; - } - convexFeature( currentFeature, processedFeatures, dissolveGeometry ); - ++processedFeatures; - } - ++jt; - } - QList values; - if ( dissolveGeometry.isNull() ) - { - QgsDebugMsg( "no dissolved geometry - should not happen" ); - return false; - } - dissolveGeometry = dissolveGeometry.convexHull(); - values = simpleMeasure( dissolveGeometry ); - QgsAttributes attributes( 3 ); - attributes[0] = QVariant( currentKey ); - attributes[1] = values.at( 0 ); - attributes[2] = values.at( 1 ); - QgsFeature dissolveFeature; - dissolveFeature.setAttributes( attributes ); - dissolveFeature.setGeometry( dissolveGeometry ); - vWriter.addFeature( dissolveFeature ); - } - //take all features - else - { - int featureCount = layer->featureCount(); - if ( p ) - { - p->setMaximum( featureCount ); - } - processedFeatures = 0; - while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - - if ( p && p->wasCanceled() ) - { - break; - } - if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) - { - continue; - } - convexFeature( currentFeature, processedFeatures, dissolveGeometry ); - ++processedFeatures; - ++jt; - } - QList values; - if ( dissolveGeometry.isNull() ) - { - QgsDebugMsg( "no dissolved geometry - should not happen" ); - return false; - } - dissolveGeometry = dissolveGeometry.convexHull(); - // values = simpleMeasure( tmpGeometry ); - values = simpleMeasure( dissolveGeometry ); - QgsAttributes attributes; - attributes[0] = QVariant( currentKey ); - attributes[1] = QVariant( values[ 0 ] ); - attributes[2] = QVariant( values[ 1 ] ); - QgsFeature dissolveFeature; - dissolveFeature.setAttributes( attributes ); - dissolveFeature.setGeometry( dissolveGeometry ); - vWriter.addFeature( dissolveFeature ); - } - } - return true; -} - - -void QgsGeometryAnalyzer::convexFeature( QgsFeature &f, int nProcessedFeatures, QgsGeometry &dissolveGeometry ) -{ - if ( !f.hasGeometry() ) - { - return; - } - - QgsGeometry featureGeometry = f.geometry(); - QgsGeometry convexGeometry = featureGeometry.convexHull(); - - if ( nProcessedFeatures == 0 ) - { - dissolveGeometry = convexGeometry; - } - else - { - dissolveGeometry = dissolveGeometry.combine( convexGeometry ); - } -} - -bool QgsGeometryAnalyzer::dissolve( QgsVectorLayer *layer, const QString &shapefileName, - bool onlySelectedFeatures, int uniqueIdField, QProgressDialog *p ) -{ - if ( !layer ) - { - return false; - } - QgsVectorDataProvider *dp = layer->dataProvider(); - if ( !dp ) - { - return false; - } - bool useField = false; - if ( uniqueIdField == -1 ) - { - uniqueIdField = 0; - } - else - { - useField = true; - } - - QgsWkbTypes::Type outputType = dp->wkbType(); - QgsCoordinateReferenceSystem crs = layer->crs(); - - QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, crs ); - QgsFeature currentFeature; - QMultiMap map; - - if ( onlySelectedFeatures ) - { - //use QgsVectorLayer::featureAtId - const QgsFeatureIds selection = layer->selectedFeatureIds(); - QgsFeatureIds::const_iterator it = selection.constBegin(); - for ( ; it != selection.constEnd(); ++it ) - { - if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) ) - { - continue; - } - map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); - } - } - else - { - QgsFeatureIterator fit = layer->getFeatures(); - while ( fit.nextFeature( currentFeature ) ) - { - map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); - } - } - - QgsGeometry dissolveGeometry; //dissolve geometry - QMultiMap::const_iterator jt = map.constBegin(); - QgsFeature outputFeature; - while ( jt != map.constEnd() ) - { - QString currentKey = jt.key(); - int processedFeatures = 0; - bool first = true; - //take only selection - if ( onlySelectedFeatures ) - { - //use QgsVectorLayer::featureAtId - const QgsFeatureIds selection = layer->selectedFeatureIds(); - if ( p ) - { - p->setMaximum( selection.size() ); - } - while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) - { - if ( p && p->wasCanceled() ) - { - break; - } - if ( selection.contains( jt.value() ) ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) - { - continue; - } - if ( first ) - { - outputFeature.setAttributes( currentFeature.attributes() ); - first = false; - } - dissolveGeometry = dissolveFeature( currentFeature, dissolveGeometry ); - ++processedFeatures; - } - ++jt; - } - } - //take all features - else - { - int featureCount = layer->featureCount(); - if ( p ) - { - p->setMaximum( featureCount ); - } - while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - - if ( p && p->wasCanceled() ) - { - break; - } - if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) - { - continue; - } - { - outputFeature.setAttributes( currentFeature.attributes() ); - first = false; - } - dissolveGeometry = dissolveFeature( currentFeature, dissolveGeometry ); - ++processedFeatures; - ++jt; - } - } - outputFeature.setGeometry( dissolveGeometry ); - vWriter.addFeature( outputFeature ); - } - return true; -} - -QgsGeometry QgsGeometryAnalyzer::dissolveFeature( const QgsFeature &f, const QgsGeometry &dissolveInto ) -{ - if ( !f.hasGeometry() ) - { - return dissolveInto; - } - - QgsGeometry featureGeometry = f.geometry(); - - if ( dissolveInto.isNull() ) - { - return featureGeometry; - } - else - { - return dissolveInto.combine( featureGeometry ); - } -} - -bool QgsGeometryAnalyzer::buffer( QgsVectorLayer *layer, const QString &shapefileName, double bufferDistance, - bool onlySelectedFeatures, bool dissolve, int bufferDistanceField, QProgressDialog *p ) -{ - if ( !layer ) - { - return false; - } - - QgsVectorDataProvider *dp = layer->dataProvider(); - if ( !dp ) - { - return false; - } - - QgsWkbTypes::Type outputType = QgsWkbTypes::Polygon; - if ( dissolve ) - { - outputType = QgsWkbTypes::MultiPolygon; - } - QgsCoordinateReferenceSystem crs = layer->crs(); - - QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, crs ); - QgsFeature currentFeature; - QgsGeometry dissolveGeometry; //dissolve geometry (if dissolve enabled) - - //take only selection - if ( onlySelectedFeatures ) - { - //use QgsVectorLayer::featureAtId - const QgsFeatureIds selection = layer->selectedFeatureIds(); - if ( p ) - { - p->setMaximum( selection.size() ); - } - - int processedFeatures = 0; - QgsFeatureIds::const_iterator it = selection.constBegin(); - for ( ; it != selection.constEnd(); ++it ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - - if ( p && p->wasCanceled() ) - { - break; - } - if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) ) - { - continue; - } - bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, dissolveGeometry, bufferDistance, bufferDistanceField ); - ++processedFeatures; - } - - if ( p ) - { - p->setValue( selection.size() ); - } - } - //take all features - else - { - QgsFeatureIterator fit = layer->getFeatures(); - - int featureCount = layer->featureCount(); - if ( p ) - { - p->setMaximum( featureCount ); - } - int processedFeatures = 0; - - while ( fit.nextFeature( currentFeature ) ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - if ( p && p->wasCanceled() ) - { - break; - } - bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, dissolveGeometry, bufferDistance, bufferDistanceField ); - ++processedFeatures; - } - if ( p ) - { - p->setValue( featureCount ); - } - } - - if ( dissolve ) - { - QgsFeature dissolveFeature; - if ( dissolveGeometry.isNull() ) - { - QgsDebugMsg( "no dissolved geometry - should not happen" ); - return false; - } - dissolveFeature.setGeometry( dissolveGeometry ); - vWriter.addFeature( dissolveFeature ); - } - return true; -} - -void QgsGeometryAnalyzer::bufferFeature( QgsFeature &f, int nProcessedFeatures, QgsVectorFileWriter *vfw, bool dissolve, - QgsGeometry &dissolveGeometry, double bufferDistance, int bufferDistanceField ) -{ - if ( !f.hasGeometry() ) - { - return; - } - - double currentBufferDistance; - QgsGeometry featureGeometry = f.geometry(); - QgsGeometry bufferGeometry; - - //create buffer - if ( bufferDistanceField == -1 ) - { - currentBufferDistance = bufferDistance; - } - else - { - currentBufferDistance = f.attribute( bufferDistanceField ).toDouble(); - } - bufferGeometry = featureGeometry.buffer( currentBufferDistance, 5 ); - - if ( dissolve ) - { - if ( nProcessedFeatures == 0 ) - { - dissolveGeometry = bufferGeometry; - } - else - { - dissolveGeometry = dissolveGeometry.combine( bufferGeometry ); - } - } - else //dissolve - { - QgsFeature newFeature; - newFeature.setGeometry( bufferGeometry ); - newFeature.setAttributes( f.attributes() ); - - //add it to vector file writer - if ( vfw ) - { - vfw->addFeature( newFeature ); - } - } -} - -bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer *lineLayer, QgsVectorLayer *eventLayer, int lineField, int eventField, QgsFeatureIds &unlocatedFeatureIds, const QString &outputLayer, - const QString &outputFormat, int locationField1, int locationField2, int offsetField, double offsetScale, - bool forceSingleGeometry, QgsVectorDataProvider *memoryProvider, QProgressDialog *p ) -{ - if ( !lineLayer || !eventLayer || !lineLayer->isValid() || !eventLayer->isValid() ) - { - return false; - } - - //create line field / id map for line layer - QMultiHash< QString, QgsFeature > lineLayerIdMap; //1:n possible (e.g. several linear reference geometries for one feature in the event layer) - QgsFeatureIterator fit = lineLayer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() << lineField ) ); - QgsFeature fet; - while ( fit.nextFeature( fet ) ) - { - lineLayerIdMap.insert( fet.attribute( lineField ).toString(), fet ); - } - - //create output datasource or attributes in memory provider - QgsVectorFileWriter *fileWriter = nullptr; - QgsFeatureList memoryProviderFeatures; - if ( !memoryProvider ) - { - QgsWkbTypes::Type memoryProviderType = QgsWkbTypes::MultiLineString; - if ( locationField2 == -1 ) - { - memoryProviderType = forceSingleGeometry ? QgsWkbTypes::Point : QgsWkbTypes::MultiPoint; - } - else - { - memoryProviderType = forceSingleGeometry ? QgsWkbTypes::LineString : QgsWkbTypes::MultiLineString; - } - fileWriter = new QgsVectorFileWriter( outputLayer, - eventLayer->dataProvider()->encoding(), - eventLayer->fields(), - memoryProviderType, - lineLayer->crs(), - outputFormat ); - } - else - { - memoryProvider->addAttributes( eventLayer->fields().toList() ); - } - - //iterate over eventLayer and write new features to output file or layer - fit = eventLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) ); - QgsGeometry lrsGeom; - double measure1, measure2 = 0.0; - - int nEventFeatures = eventLayer->featureCount(); - int featureCounter = 0; - int nOutputFeatures = 0; //number of output features for the current event feature - if ( p ) - { - p->setWindowModality( Qt::WindowModal ); - p->setMinimum( 0 ); - p->setMaximum( nEventFeatures ); - p->show(); - } - - while ( fit.nextFeature( fet ) ) - { - nOutputFeatures = 0; - - //update progress dialog - if ( p ) - { - if ( p->wasCanceled() ) - { - break; - } - p->setValue( featureCounter ); - ++featureCounter; - } - - measure1 = fet.attribute( locationField1 ).toDouble(); - if ( locationField2 != -1 ) - { - measure2 = fet.attribute( locationField2 ).toDouble(); - if ( qgsDoubleNear( ( measure2 - measure1 ), 0.0 ) ) - { - continue; - } - } - - QList featureIdList = lineLayerIdMap.values( fet.attribute( eventField ).toString() ); - QList::iterator featureIdIt = featureIdList.begin(); - for ( ; featureIdIt != featureIdList.end(); ++featureIdIt ) - { - if ( locationField2 == -1 ) - { - lrsGeom = locateAlongMeasure( measure1, featureIdIt->geometry() ); - } - else - { - lrsGeom = locateBetweenMeasures( measure1, measure2, featureIdIt->geometry() ); - } - - if ( !lrsGeom.isNull() ) - { - ++nOutputFeatures; - addEventLayerFeature( fet, lrsGeom, featureIdIt->geometry(), fileWriter, memoryProviderFeatures, offsetField, offsetScale, forceSingleGeometry ); - } - } - if ( nOutputFeatures < 1 ) - { - unlocatedFeatureIds.insert( fet.id() ); - } - } - - if ( p ) - { - p->setValue( nEventFeatures ); - } - - if ( memoryProvider ) - { - memoryProvider->addFeatures( memoryProviderFeatures ); - } - delete fileWriter; - return true; -} - -void QgsGeometryAnalyzer::addEventLayerFeature( QgsFeature &feature, const QgsGeometry &geom, const QgsGeometry &lineGeom, QgsVectorFileWriter *fileWriter, QgsFeatureList &memoryFeatures, - int offsetField, double offsetScale, bool forceSingleType ) -{ - if ( geom.isNull() ) - { - return; - } - - QList geomList; - if ( forceSingleType ) - { - geomList = geom.asGeometryCollection(); - } - else - { - geomList.push_back( geom ); - } - - QList::iterator geomIt = geomList.begin(); - for ( ; geomIt != geomList.end(); ++geomIt ) - { - //consider offset - QgsGeometry newGeom = *geomIt; - if ( offsetField >= 0 ) - { - double offsetVal = feature.attribute( offsetField ).toDouble(); - offsetVal *= offsetScale; - newGeom = createOffsetGeometry( *geomIt, lineGeom, offsetVal ); - if ( newGeom.isNull() ) - { - continue; - } - } - - feature.setGeometry( newGeom ); - if ( fileWriter ) - { - fileWriter->addFeature( feature ); - } - else - { - memoryFeatures << feature; - } - } -} - -QgsGeometry QgsGeometryAnalyzer::createOffsetGeometry( const QgsGeometry &geom, const QgsGeometry &lineGeom, double offset ) -{ - if ( !geom || lineGeom.isNull() ) - { - return QgsGeometry(); - } - - QList inputGeomList; - - if ( geom.isMultipart() ) - { - inputGeomList = geom.asGeometryCollection(); - } - else - { - inputGeomList.push_back( geom ); - } - - QList outputGeomList; - QList::const_iterator inputGeomIt = inputGeomList.constBegin(); - GEOSContextHandle_t geosctxt = QgsGeometry::getGEOSHandler(); - for ( ; inputGeomIt != inputGeomList.constEnd(); ++inputGeomIt ) - { - if ( geom.type() == QgsWkbTypes::LineGeometry ) - { - GEOSGeometry *inputGeomItGeos = inputGeomIt->exportToGeos(); - GEOSGeometry *offsetGeom = GEOSOffsetCurve_r( geosctxt, inputGeomItGeos, -offset, 8 /*quadSegments*/, 0 /*joinStyle*/, 5.0 /*miterLimit*/ ); - GEOSGeom_destroy_r( geosctxt, inputGeomItGeos ); - if ( !offsetGeom || !GEOSisValid_r( geosctxt, offsetGeom ) ) - { - return QgsGeometry(); - } - if ( !GEOSisValid_r( geosctxt, offsetGeom ) || GEOSGeomTypeId_r( geosctxt, offsetGeom ) != GEOS_LINESTRING || GEOSGeomGetNumPoints_r( geosctxt, offsetGeom ) < 1 ) - { - GEOSGeom_destroy_r( geosctxt, offsetGeom ); - return QgsGeometry(); - } - outputGeomList.push_back( offsetGeom ); - } - else if ( geom.type() == QgsWkbTypes::PointGeometry ) - { - QgsPointXY p = ( *inputGeomIt ).asPoint(); - p = createPointOffset( p.x(), p.y(), offset, lineGeom ); - GEOSCoordSequence *ptSeq = GEOSCoordSeq_create_r( geosctxt, 1, 2 ); - GEOSCoordSeq_setX_r( geosctxt, ptSeq, 0, p.x() ); - GEOSCoordSeq_setY_r( geosctxt, ptSeq, 0, p.y() ); - GEOSGeometry *geosPt = GEOSGeom_createPoint_r( geosctxt, ptSeq ); - outputGeomList.push_back( geosPt ); - } - } - - QgsGeometry outGeometry; - if ( !geom.isMultipart() ) - { - GEOSGeometry *outputGeom = outputGeomList.at( 0 ); - if ( outputGeom ) - { - outGeometry.fromGeos( outputGeom ); - } - } - else - { - GEOSGeometry **geomArray = new GEOSGeometry*[outputGeomList.size()]; - for ( int i = 0; i < outputGeomList.size(); ++i ) - { - geomArray[i] = outputGeomList.at( i ); - } - GEOSGeometry *collection = nullptr; - if ( geom.type() == QgsWkbTypes::PointGeometry ) - { - collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOINT, geomArray, outputGeomList.size() ); - } - else if ( geom.type() == QgsWkbTypes::LineGeometry ) - { - collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTILINESTRING, geomArray, outputGeomList.size() ); - } - outGeometry.fromGeos( collection ); - delete[] geomArray; - } - return outGeometry; -} - -QgsPointXY QgsGeometryAnalyzer::createPointOffset( double x, double y, double dist, const QgsGeometry &lineGeom ) const -{ - QgsPointXY p( x, y ); - QgsPointXY minDistPoint; - int afterVertexNr; - lineGeom.closestSegmentWithContext( p, minDistPoint, afterVertexNr ); - - int beforeVertexNr = afterVertexNr - 1; - QgsPointXY beforeVertex = lineGeom.vertexAt( beforeVertexNr ); - QgsPointXY afterVertex = lineGeom.vertexAt( afterVertexNr ); - - //get normal vector - double dx = afterVertex.x() - beforeVertex.x(); - double dy = afterVertex.y() - beforeVertex.y(); - double normalX = -dy; - double normalY = dx; //#spellok - double normalLength = sqrt( normalX * normalX + normalY * normalY ); //#spellok - normalX *= ( dist / normalLength ); - normalY *= ( dist / normalLength ); //#spellok - - double debugLength = sqrt( normalX * normalX + normalY * normalY ); //control //#spellok - Q_UNUSED( debugLength ); - return QgsPointXY( x - normalX, y - normalY ); //negative values -> left side, positive values -> right side //#spellok -} - -QgsGeometry QgsGeometryAnalyzer::locateBetweenMeasures( double fromMeasure, double toMeasure, const QgsGeometry &lineGeom ) -{ - if ( lineGeom.isNull() ) - { - return QgsGeometry(); - } - - QgsMultiPolyline resultGeom; - - //need to go with WKB and z coordinate until QgsGeometry supports M values - QByteArray wkb( lineGeom.exportToWkb() ); - QgsConstWkbPtr wkbPtr( wkb ); - wkbPtr.readHeader(); - - QgsWkbTypes::Type wkbType = lineGeom.wkbType(); - if ( wkbType != QgsWkbTypes::LineString25D && wkbType != QgsWkbTypes::MultiLineString25D ) - { - return QgsGeometry(); - } - - if ( wkbType == QgsWkbTypes::LineString25D ) - { - locateBetweenWkbString( wkbPtr, resultGeom, fromMeasure, toMeasure ); - } - else if ( wkbType == QgsWkbTypes::MultiLineString25D ) - { - int nLines; - wkbPtr >> nLines; - for ( int i = 0; i < nLines; ++i ) - { - wkbPtr.readHeader(); - wkbPtr = locateBetweenWkbString( wkbPtr, resultGeom, fromMeasure, toMeasure ); - } - } - - if ( resultGeom.size() < 1 ) - { - return QgsGeometry(); - } - return QgsGeometry::fromMultiPolyline( resultGeom ); -} - -QgsGeometry QgsGeometryAnalyzer::locateAlongMeasure( double measure, const QgsGeometry &lineGeom ) -{ - if ( lineGeom.isNull() ) - { - return QgsGeometry(); - } - - QgsMultiPoint resultGeom; - - //need to go with WKB and z coordinate until QgsGeometry supports M values - QByteArray wkb( lineGeom.exportToWkb() ); - QgsConstWkbPtr wkbPtr( wkb ); - QgsWkbTypes::Type wkbType = lineGeom.wkbType(); - - if ( wkbType != QgsWkbTypes::LineString25D && wkbType != QgsWkbTypes::MultiLineString25D ) - { - return QgsGeometry(); - } - - if ( wkbType == QgsWkbTypes::LineString25D ) - { - locateAlongWkbString( wkbPtr, resultGeom, measure ); - } - else if ( wkbType == QgsWkbTypes::MultiLineString25D ) - { - int nLines; - wkbPtr >> nLines; - for ( int i = 0; i < nLines; ++i ) - { - wkbPtr.readHeader(); - wkbPtr = locateAlongWkbString( wkbPtr, resultGeom, measure ); - } - } - - if ( resultGeom.size() < 1 ) - { - return QgsGeometry(); - } - - return QgsGeometry::fromMultiPoint( resultGeom ); -} - -QgsConstWkbPtr QgsGeometryAnalyzer::locateBetweenWkbString( QgsConstWkbPtr wkbPtr, QgsMultiPolyline &result, double fromMeasure, double toMeasure ) -{ - int nPoints; - wkbPtr >> nPoints; - - QgsPolyline currentLine; - double prevx = 0.0, prevy = 0.0, prevz = 0.0; - for ( int i = 0; i < nPoints; ++i ) - { - double x, y, z; - wkbPtr >> x >> y >> z; - - if ( i > 0 ) - { - QgsPointXY pt1, pt2; - bool secondPointClipped; //true if second point is != segment endpoint - bool measureInSegment = clipSegmentByRange( prevx, prevy, prevz, x, y, z, fromMeasure, toMeasure, pt1, pt2, secondPointClipped ); - if ( measureInSegment ) - { - if ( currentLine.size() < 1 ) //no points collected yet, so the first point needs to be added to the line - { - currentLine.append( pt1 ); - } - - if ( pt1 != pt2 ) //avoid duplicated entry if measure value equals m-value of vertex - { - currentLine.append( pt2 ); - } - - if ( secondPointClipped || i == nPoints - 1 ) //close current segment - { - if ( currentLine.size() > 1 ) - { - result.append( currentLine ); - } - currentLine.clear(); - } - } - } - prevx = x; - prevy = y; - prevz = z; - } - - return wkbPtr; -} - -QgsConstWkbPtr QgsGeometryAnalyzer::locateAlongWkbString( QgsConstWkbPtr wkbPtr, QgsMultiPoint &result, double measure ) -{ - int nPoints; - wkbPtr >> nPoints; - - double x, y, z; - double prevx = 0.0, prevy = 0.0, prevz = 0.0; - for ( int i = 0; i < nPoints; ++i ) - { - wkbPtr >> x >> y >> z; - - if ( i > 0 ) - { - QgsPointXY pt1, pt2; - bool pt1Ok, pt2Ok; - locateAlongSegment( prevx, prevy, prevz, x, y, z, measure, pt1Ok, pt1, pt2Ok, pt2 ); - if ( pt1Ok ) - { - result.append( pt1 ); - } - if ( pt2Ok && i == nPoints - 1 ) - { - result.append( pt2 ); - } - } - prevx = x; - prevy = y; - prevz = z; - } - - return wkbPtr; -} - -bool QgsGeometryAnalyzer::clipSegmentByRange( double x1, double y1, double m1, double x2, double y2, double m2, double range1, double range2, QgsPointXY &pt1, - QgsPointXY &pt2, bool &secondPointClipped ) -{ - bool reversed = m1 > m2; - double tmp; - - //reverse m1, m2 if necessary (and consequently also x1,x2 / y1, y2) - if ( reversed ) - { - tmp = m1; - m1 = m2; - m2 = tmp; - - tmp = x1; - x1 = x2; - x2 = tmp; - - tmp = y1; - y1 = y2; - y2 = tmp; - } - - //reverse range1, range2 if necessary - if ( range1 > range2 ) - { - tmp = range1; - range1 = range2; - range2 = tmp; - } - - //segment completely outside of range - if ( m2 < range1 || m1 > range2 ) - { - return false; - } - - //segment completely inside of range - if ( m2 <= range2 && m1 >= range1 ) - { - if ( reversed ) - { - pt1.setX( x2 ); - pt1.setY( y2 ); - pt2.setX( x1 ); - pt2.setY( y1 ); - } - else - { - pt1.setX( x1 ); - pt1.setY( y1 ); - pt2.setX( x2 ); - pt2.setY( y2 ); - } - secondPointClipped = false; - return true; - } - - //m1 inside and m2 not - if ( m1 >= range1 && m1 <= range2 ) - { - pt1.setX( x1 ); - pt1.setY( y1 ); - double dist = ( range2 - m1 ) / ( m2 - m1 ); - pt2.setX( x1 + ( x2 - x1 ) * dist ); - pt2.setY( y1 + ( y2 - y1 ) * dist ); - secondPointClipped = !reversed; - } - - //m2 inside and m1 not - if ( m2 >= range1 && m2 <= range2 ) - { - pt2.setX( x2 ); - pt2.setY( y2 ); - double dist = ( m2 - range1 ) / ( m2 - m1 ); - pt1.setX( x2 - ( x2 - x1 ) * dist ); - pt1.setY( y2 - ( y2 - y1 ) * dist ); - secondPointClipped = reversed; - } - - //range1 and range 2 both inside the segment - if ( range1 >= m1 && range2 <= m2 ) - { - double dist1 = ( range1 - m1 ) / ( m2 - m1 ); - double dist2 = ( range2 - m1 ) / ( m2 - m1 ); - pt1.setX( x1 + ( x2 - x1 ) * dist1 ); - pt1.setY( y1 + ( y2 - y1 ) * dist1 ); - pt2.setX( x1 + ( x2 - x1 ) * dist2 ); - pt2.setY( y1 + ( y2 - y1 ) * dist2 ); - secondPointClipped = true; - } - - if ( reversed ) //switch p1 and p2 - { - QgsPointXY tmpPt = pt1; - pt1 = pt2; - pt2 = tmpPt; - } - - return true; -} - -void QgsGeometryAnalyzer::locateAlongSegment( double x1, double y1, double m1, double x2, double y2, double m2, double measure, bool &pt1Ok, QgsPointXY &pt1, bool &pt2Ok, QgsPointXY &pt2 ) -{ - bool reversed = false; - pt1Ok = false; - pt2Ok = false; - double tolerance = 0.000001; //work with a small tolerance to catch e.g. locations at endpoints - - if ( m1 > m2 ) - { - double tmp = m1; - m1 = m2; - m2 = tmp; - reversed = true; - } - - //segment does not match - if ( ( m1 - measure ) > tolerance || ( measure - m2 ) > tolerance ) - { - pt1Ok = false; - pt2Ok = false; - return; - } - - //match with vertex1 - if ( qgsDoubleNear( m1, measure, tolerance ) ) - { - if ( reversed ) - { - pt2Ok = true; - pt2.setX( x2 ); - pt2.setY( y2 ); - } - else - { - pt1Ok = true; - pt1.setX( x1 ); - pt1.setY( y1 ); - } - } - - //match with vertex2 - if ( qgsDoubleNear( m2, measure, tolerance ) ) - { - if ( reversed ) - { - pt1Ok = true; - pt1.setX( x1 ); - pt1.setY( y1 ); - } - else - { - pt2Ok = true; - pt2.setX( x2 ); - pt2.setY( y2 ); - } - } - - - if ( pt1Ok || pt2Ok ) - { - return; - } - - //match between the vertices - if ( qgsDoubleNear( m1, m2 ) ) - { - pt1.setX( x1 ); - pt1.setY( y1 ); - pt1Ok = true; - return; - } - double dist = ( measure - m1 ) / ( m2 - m1 ); - if ( reversed ) - { - dist = 1 - dist; - } - - pt1.setX( x1 + dist * ( x2 - x1 ) ); - pt1.setY( y1 + dist * ( y2 - y1 ) ); - pt1Ok = true; -} diff --git a/src/analysis/vector/qgsgeometryanalyzer.h b/src/analysis/vector/qgsgeometryanalyzer.h deleted file mode 100644 index c7111f53aa8..00000000000 --- a/src/analysis/vector/qgsgeometryanalyzer.h +++ /dev/null @@ -1,161 +0,0 @@ -/*************************************************************************** - qgsgeometryanalyzer.h - QGIS Tools for vector geometry analysis - ------------------- - begin : 19 March 2009 - copyright : (C) Carson Farmer - email : carson.farmer@gmail.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 QGSGEOMETRYANALYZERH -#define QGSGEOMETRYANALYZERH - -#include "qgsfeature.h" -#include "qgsgeometry.h" -#include "qgis_analysis.h" - -class QgsVectorFileWriter; -class QProgressDialog; -class QgsVectorDataProvider; -class QgsDistanceArea; - -/** \ingroup analysis - * The QGis class provides vector geometry analysis functions - */ - -class ANALYSIS_EXPORT QgsGeometryAnalyzer -{ - public: - - /** Simplify vector layer using (a modified) Douglas-Peucker algorithm - * and write it to a new shape file - * \param layer input vector layer - * \param shapefileName path to the output shp - * \param tolerance (level of simplification) - * \param onlySelectedFeatures if true, only selected features are considered, else all the features - * \param p progress dialog (or 0 if no progress dialog is to be shown) - */ - bool simplify( QgsVectorLayer *layer, const QString &shapefileName, double tolerance, - bool onlySelectedFeatures = false, QProgressDialog *p = nullptr ); - - /** Calculate the true centroids, or 'center of mass' for a vector layer and - * write it to a new shape file - * \param layer input vector layer - * \param shapefileName path to the output shp - * \param onlySelectedFeatures if true, only selected features are considered, else all the features - * \param p progress dialog (or 0 if no progress dialog is to be shown) - */ - bool centroids( QgsVectorLayer *layer, const QString &shapefileName, - bool onlySelectedFeatures = false, QProgressDialog *p = nullptr ); - - /** Create a polygon based on the extent of all (selected) features and write it to a new shape file - * \param layer input vector layer - * \param shapefileName path to the output shp - * \param onlySelectedFeatures if true, only selected features are considered, else all the features - * \param p progress dialog (or 0 if no progress dialog is to be shown) - */ - bool extent( QgsVectorLayer *layer, const QString &shapefileName, bool onlySelectedFeatures = false, QProgressDialog *p = 0 ); - - /** Create buffers for a vector layer and write it to a new shape file - * \param layer input vector layer - * \param shapefileName path to the output shp - * \param bufferDistance distance for buffering (if no buffer field is specified) - * \param onlySelectedFeatures if true, only selected features are considered, else all the features - * \param dissolve if true, merge all the buffers to a big multipolygon - * \param bufferDistanceField index of the attribute field that contains the buffer distance (or -1 if all features have the same buffer distance) - * \param p progress dialog (or 0 if no progress dialog is to be shown) - */ - bool buffer( QgsVectorLayer *layer, const QString &shapefileName, double bufferDistance, - bool onlySelectedFeatures = false, bool dissolve = false, int bufferDistanceField = -1, QProgressDialog *p = nullptr ); - - /** Create convex hull(s) of a vector layer and write it to a new shape file - * \param layer input vector layer - * \param shapefileName path to the output shp - * \param onlySelectedFeatures if true, only selected features are considered, else all the features - * \param uniqueIdField index of the attribute field that contains the unique convex hull id (or -1 if - * all features have the same buffer distance) - * \param p progress dialog (or 0 if no progress dialog is to be shown) - */ - bool convexHull( QgsVectorLayer *layer, const QString &shapefileName, bool onlySelectedFeatures = false, - int uniqueIdField = -1, QProgressDialog *p = nullptr ); - - /** Dissolve a vector layer and write it to a new shape file - * \param layer input vector layer - * \param shapefileName path to the output shp - * \param onlySelectedFeatures if true, only selected features are considered, else all the features - * \param uniqueIdField index of the attribute field that contains the unique id to dissolve on (or -1 if - * all features should be dissolved together) - * \param p progress dialog (or 0 if no progress dialog is to be shown) - */ - bool dissolve( QgsVectorLayer *layer, const QString &shapefileName, bool onlySelectedFeatures = false, - int uniqueIdField = -1, QProgressDialog *p = nullptr ); - - /** Creates an event layer (multipoint or multiline) by locating features from a (non-spatial) event table along the features of a line layer. - * Note that currently (until QgsGeometry supports m-values) the z-coordinate of the line layer is used for linear referencing - * \param lineLayer layer with the line geometry - * \param eventLayer layer with features and location field - * \param lineField join index in line layer - * \param eventField join index in event layer - * \param outputLayer name of output file (can be empty if a memory layer is used) - * \param outputFormat name of output format (can be empty if a memory provider is used to store the results) - * \param unlocatedFeatureIds out: ids of event features where linear referencing was not successful - * \param locationField1 attribute index of location field in event layer - * \param locationField2 attribute index of location end field (or -1 for point layer) - * \param offsetField attribute index for offset field. Negative offset value = offset to left side, positive value = offset to right side - * \param offsetScale factor to scale offset - * \param forceSingleGeometry force layer to single point/line type. Feature attributes are copied in case of multiple matches - * \param memoryProvider memory provider to write output to (can be 0 if output is written to a file) - * \param p progress dialog or 0 if no progress dialog should be shown - */ - bool eventLayer( QgsVectorLayer *lineLayer, QgsVectorLayer *eventLayer, int lineField, int eventField, QgsFeatureIds &unlocatedFeatureIds SIP_OUT, const QString &outputLayer, - const QString &outputFormat, int locationField1, int locationField2 = -1, int offsetField = -1, double offsetScale = 1.0, - bool forceSingleGeometry = false, QgsVectorDataProvider *memoryProvider = nullptr, QProgressDialog *p = nullptr ); - - //! Returns linear reference geometry as a multiline (or 0 if no match). Currently, the z-coordinates are considered to be the measures (no support for m-values in QGIS) - QgsGeometry locateBetweenMeasures( double fromMeasure, double toMeasure, const QgsGeometry &lineGeom ); - - /** Returns linear reference geometry. Unlike the PostGIS function, this method always returns multipoint or 0 if no match (not geometry collection). - * Currently, the z-coordinates are considered to be the measures (no support for m-values in QGIS) - */ - QgsGeometry locateAlongMeasure( double measure, const QgsGeometry &lineGeom ); - - private: - - QList simpleMeasure( QgsGeometry &geometry ); - double perimeterMeasure( const QgsGeometry &geometry, QgsDistanceArea &measure ); - //! Helper function to simplify an individual feature - void simplifyFeature( QgsFeature &f, QgsVectorFileWriter *vfw, double tolerance ); - //! Helper function to get the cetroid of an individual feature - void centroidFeature( QgsFeature &f, QgsVectorFileWriter *vfw ); - //! Helper function to buffer an individual feature - void bufferFeature( QgsFeature &f, int nProcessedFeatures, QgsVectorFileWriter *vfw, bool dissolve, QgsGeometry &dissolveGeometry, - double bufferDistance, int bufferDistanceField ); - //! Helper function to get the convex hull of feature(s) - void convexFeature( QgsFeature &f, int nProcessedFeatures, QgsGeometry &dissolveGeometry ); - //! Helper function to dissolve feature(s) - QgsGeometry dissolveFeature( const QgsFeature &f, const QgsGeometry &dissolveInto ); - - //helper functions for event layer - void addEventLayerFeature( QgsFeature &feature, const QgsGeometry &geom, const QgsGeometry &lineGeom, QgsVectorFileWriter *fileWriter, QgsFeatureList &memoryFeatures, int offsetField = -1, double offsetScale = 1.0, - bool forceSingleType = false ); - - /** Create geometry offset relative to line geometry. - \param geom the geometry to modify - \param lineGeom the line geometry to which the feature is referenced - \param offset the offset value in layer unit. Negative values mean offset towards left, positive values offset to the right side*/ - QgsGeometry createOffsetGeometry( const QgsGeometry &geom, const QgsGeometry &lineGeom, double offset ); - QgsPointXY createPointOffset( double x, double y, double dist, const QgsGeometry &lineGeom ) const; - QgsConstWkbPtr locateBetweenWkbString( QgsConstWkbPtr ptr, QgsMultiPolyline &result, double fromMeasure, double toMeasure ); - QgsConstWkbPtr locateAlongWkbString( QgsConstWkbPtr ptr, QgsMultiPoint &result, double measure ); - static bool clipSegmentByRange( double x1, double y1, double m1, double x2, double y2, double m2, double range1, double range2, QgsPointXY &pt1, QgsPointXY &pt2, bool &secondPointClipped ); - static void locateAlongSegment( double x1, double y1, double m1, double x2, double y2, double m2, double measure, bool &pt1Ok, QgsPointXY &pt1, bool &pt2Ok, QgsPointXY &pt2 ); -}; -#endif //QGSVECTORANALYZER diff --git a/src/analysis/vector/qgsoverlayanalyzer.cpp b/src/analysis/vector/qgsoverlayanalyzer.cpp deleted file mode 100644 index 39049e7176c..00000000000 --- a/src/analysis/vector/qgsoverlayanalyzer.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/*************************************************************************** - qgsoverlayanalyzer.cpp - QGIS Tools for vector geometry analysis - ------------------- - begin : 8 Nov 2009 - copyright : (C) Carson J. Q. Farmer - email : carson.farmer@gmail.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 "qgsoverlayanalyzer.h" - -#include "qgsapplication.h" -#include "qgsfeatureiterator.h" -#include "qgsfields.h" -#include "qgsfeature.h" -#include "qgsgeometry.h" -#include "qgslogger.h" -#include "qgscoordinatereferencesystem.h" -#include "qgsspatialindex.h" -#include "qgsvectorfilewriter.h" -#include "qgsvectordataprovider.h" -#include "qgsdistancearea.h" -#include - -bool QgsOverlayAnalyzer::intersection( QgsVectorLayer *layerA, QgsVectorLayer *layerB, - const QString &shapefileName, bool onlySelectedFeatures, - QProgressDialog *p ) -{ - if ( !layerA || !layerB ) - { - return false; - } - - QgsVectorDataProvider *dpA = layerA->dataProvider(); - QgsVectorDataProvider *dpB = layerB->dataProvider(); - if ( !dpA || !dpB ) - { - return false; - } - - QgsWkbTypes::Type outputType = dpA->wkbType(); - QgsCoordinateReferenceSystem crs = layerA->crs(); - QgsFields fieldsA = layerA->fields(); - QgsFields fieldsB = layerB->fields(); - combineFieldLists( fieldsA, fieldsB ); - - QgsVectorFileWriter vWriter( shapefileName, dpA->encoding(), fieldsA, outputType, crs ); - QgsFeature currentFeature; - - //take only selection - if ( onlySelectedFeatures ) - { - QgsFeatureIds selectionB = layerB->selectedFeatureIds(); - QgsFeatureRequest req = QgsFeatureRequest().setFilterFids( selectionB ).setSubsetOfAttributes( QgsAttributeList() ); - QgsSpatialIndex index = QgsSpatialIndex( layerB->getFeatures( req ) ); - - //use QgsVectorLayer::featureAtId - const QgsFeatureIds selectionA = layerA->selectedFeatureIds(); - if ( p ) - { - p->setMaximum( selectionA.size() ); - } - req = QgsFeatureRequest().setFilterFids( selectionA ); - QgsFeatureIterator selectionAIt = layerA->getFeatures( req ); - QgsFeature currentFeature; - int processedFeatures = 0; - while ( selectionAIt.nextFeature( currentFeature ) ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - - if ( p && p->wasCanceled() ) - { - break; - } - - intersectFeature( currentFeature, &vWriter, layerB, &index ); - ++processedFeatures; - } - - if ( p ) - { - p->setValue( selectionA.size() ); - } - } - //take all features - else - { - QgsFeatureRequest req = QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ); - QgsSpatialIndex index = QgsSpatialIndex( layerB->getFeatures( req ) ); - - int featureCount = layerA->featureCount(); - if ( p ) - { - p->setMaximum( featureCount ); - } - int processedFeatures = 0; - - QgsFeatureIterator fit = layerA->getFeatures(); - - QgsFeature currentFeature; - while ( fit.nextFeature( currentFeature ) ) - { - if ( p ) - { - p->setValue( processedFeatures ); - } - if ( p && p->wasCanceled() ) - { - break; - } - intersectFeature( currentFeature, &vWriter, layerB, &index ); - ++processedFeatures; - } - if ( p ) - { - p->setValue( featureCount ); - } - } - return true; -} - -void QgsOverlayAnalyzer::intersectFeature( QgsFeature &f, QgsVectorFileWriter *vfw, - QgsVectorLayer *vl, QgsSpatialIndex *index ) -{ - if ( !f.hasGeometry() ) - { - return; - } - - QgsGeometry featureGeometry = f.geometry(); - QgsGeometry intersectGeometry; - QgsFeature overlayFeature; - - QList intersects = index->intersects( featureGeometry.boundingBox() ); - QgsFeatureRequest req = QgsFeatureRequest().setFilterFids( intersects.toSet() ); - QgsFeatureIterator intersectIt = vl->getFeatures( req ); - QgsFeature outFeature; - while ( intersectIt.nextFeature( overlayFeature ) ) - { - if ( featureGeometry.intersects( overlayFeature.geometry() ) ) - { - intersectGeometry = featureGeometry.intersection( overlayFeature.geometry() ); - - outFeature.setGeometry( intersectGeometry ); - QgsAttributes attributesA = f.attributes(); - QgsAttributes attributesB = overlayFeature.attributes(); - combineAttributeMaps( attributesA, attributesB ); - outFeature.setAttributes( attributesA ); - - //add it to vector file writer - if ( vfw ) - { - vfw->addFeature( outFeature ); - } - } - } -} - -void QgsOverlayAnalyzer::combineFieldLists( QgsFields &fieldListA, const QgsFields &fieldListB ) -{ - QList names; - Q_FOREACH ( const QgsField &field, fieldListA ) - names.append( field.name() ); - - for ( int idx = 0; idx < fieldListB.count(); ++idx ) - { - QgsField field = fieldListB.at( idx ); - int count = 0; - while ( names.contains( field.name() ) ) - { - QString name = QStringLiteral( "%1_%2" ).arg( field.name() ).arg( count ); - field = QgsField( name, field.type() ); - ++count; - } - fieldListA.append( field ); - names.append( field.name() ); - } -} - -void QgsOverlayAnalyzer::combineAttributeMaps( QgsAttributes &attributesA, const QgsAttributes &attributesB ) -{ - attributesA += attributesB; -} - diff --git a/src/analysis/vector/qgsoverlayanalyzer.h b/src/analysis/vector/qgsoverlayanalyzer.h deleted file mode 100644 index 0fd9ada8a49..00000000000 --- a/src/analysis/vector/qgsoverlayanalyzer.h +++ /dev/null @@ -1,54 +0,0 @@ -/*************************************************************************** - qgsoverlayanalyzer.h - QGIS Tools for vector geometry analysis - ------------------- - begin : 19 March 2009 - copyright : (C) Carson Farmer - email : carson.farmer@gmail.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 QGSOVERLAYANALYZERH -#define QGSOVERLAYANALYZERH - -#include "qgsvectorlayer.h" -#include "qgis_analysis.h" - -class QgsVectorFileWriter; -class QProgressDialog; -class QgsSpatialIndex; - -/** \ingroup analysis - * The QGis class provides vector overlay analysis functions - */ - -class ANALYSIS_EXPORT QgsOverlayAnalyzer -{ - public: - - /** Perform an intersection on two input vector layers and write output to a new shape file - \param layerA input vector layer - \param layerB input vector layer - \param shapefileName path to the output shp - \param onlySelectedFeatures if true, only selected features are considered, else all the features - \param p progress dialog (or 0 if no progress dialog is to be shown) - */ - bool intersection( QgsVectorLayer *layerA, QgsVectorLayer *layerB, - const QString &shapefileName, bool onlySelectedFeatures = false, - QProgressDialog *p = nullptr ); - - private: - - void combineFieldLists( QgsFields &fieldListA, const QgsFields &fieldListB ); - void intersectFeature( QgsFeature &f, QgsVectorFileWriter *vfw, QgsVectorLayer *dp, QgsSpatialIndex *index ); - void combineAttributeMaps( QgsAttributes &attributesA, const QgsAttributes &attributesB ); -}; - -#endif //QGSVECTORANALYZER diff --git a/src/analysis/vector/qgspointsample.cpp b/src/analysis/vector/qgspointsample.cpp deleted file mode 100644 index 528ecbca227..00000000000 --- a/src/analysis/vector/qgspointsample.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/*************************************************************************** - qgspointsample.cpp - --------------------- - begin : July 2013 - copyright : (C) 2013 by Marco Hugentobler - email : marco dot hugentobler at sourcepole dot ch - *************************************************************************** - * * - * 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 "qgspointsample.h" -#include "qgsfeatureiterator.h" -#include "qgsgeometry.h" -#include "qgsspatialindex.h" -#include "qgsvectorfilewriter.h" -#include "qgsvectorlayer.h" -#include -#include "mersenne-twister.h" - - -QgsPointSample::QgsPointSample( QgsVectorLayer *inputLayer, const QString &outputLayer, const QString &nPointsAttribute, const QString &minDistAttribute ): mInputLayer( inputLayer ), - mOutputLayer( outputLayer ), mNumberOfPointsAttribute( nPointsAttribute ), mMinDistanceAttribute( minDistAttribute ), mNCreatedPoints( 0 ) -{ -} - -QgsPointSample::QgsPointSample() - : mInputLayer( nullptr ) - , mNCreatedPoints( 0 ) -{ -} - -int QgsPointSample::createRandomPoints( QProgressDialog *pd ) -{ - Q_UNUSED( pd ); - - //create input layer from id (test if polygon, valid) - if ( !mInputLayer ) - { - return 1; - } - - if ( mInputLayer->geometryType() != QgsWkbTypes::PolygonGeometry ) - { - 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( QStringLiteral( "id" ), QVariant::Int ) ); - outputFields.append( QgsField( QStringLiteral( "station_id" ), QVariant::Int ) ); - outputFields.append( QgsField( QStringLiteral( "stratum_id" ), QVariant::Int ) ); - QgsVectorFileWriter writer( mOutputLayer, QStringLiteral( "UTF-8" ), - outputFields, - QgsWkbTypes::Point, - mInputLayer->crs() ); - - //check if creation of output layer successful - 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->fields() ) ); - 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 ) -{ - if ( !inputFeature.hasGeometry() ) - return; - - QgsGeometry geom = inputFeature.geometry(); - QgsRectangle geomRect = geom.boundingBox(); - if ( geomRect.isEmpty() ) - { - return; - } - - QgsSpatialIndex sIndex; //to check minimum distance - QMap< QgsFeatureId, QgsPointXY > 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() / MD_RAND_MAX ) * geomRect.width() + geomRect.xMinimum(); - randY = ( ( double )mt_rand() / MD_RAND_MAX ) * geomRect.height() + geomRect.yMinimum(); - QgsPointXY 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( QStringLiteral( "id" ), mNCreatedPoints + 1 ); - f.setAttribute( QStringLiteral( "station_id" ), points + 1 ); - f.setAttribute( QStringLiteral( "stratum_id" ), inputFeature.id() ); - f.setGeometry( ptGeom ); - writer.addFeature( f ); - sIndex.insertFeature( f ); - pointMapForFeature.insert( mNCreatedPoints, randPoint ); - ++points; - ++mNCreatedPoints; - } - ++nIterations; - } -} - -bool QgsPointSample::checkMinDistance( QgsPointXY &pt, QgsSpatialIndex &index, double minDistance, QMap< QgsFeatureId, QgsPointXY > &pointMap ) -{ - if ( minDistance <= 0 ) - { - return true; - } - - QList neighborList = index.nearestNeighbor( pt, 1 ); - if ( neighborList.isEmpty() ) - { - return true; - } - - QMap< QgsFeatureId, QgsPointXY >::const_iterator it = pointMap.find( neighborList[0] ); - if ( it == pointMap.constEnd() ) //should not happen - { - return true; - } - - QgsPointXY neighborPt = it.value(); - if ( neighborPt.sqrDist( pt ) < ( minDistance * minDistance ) ) - { - return false; - } - return true; -} - - - - diff --git a/src/analysis/vector/qgspointsample.h b/src/analysis/vector/qgspointsample.h deleted file mode 100644 index 098838c9f3e..00000000000 --- a/src/analysis/vector/qgspointsample.h +++ /dev/null @@ -1,57 +0,0 @@ -/*************************************************************************** - qgspointsample.h - --------------------- - begin : July 2013 - copyright : (C) 2013 by Marco Hugentobler - email : marco dot hugentobler at sourcepole dot ch - *************************************************************************** - * * - * 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 QGSPOINTSAMPLE_H -#define QGSPOINTSAMPLE_H - -#include "qgsfeature.h" -#include -#include "qgis_analysis.h" - -class QgsFeature; -class QgsPointXY; -class QgsSpatialIndex; -class QgsVectorFileWriter; -class QgsVectorLayer; -class QProgressDialog; - -/** \ingroup analysis - * Creates random points in polygons / multipolygons*/ -class ANALYSIS_EXPORT QgsPointSample -{ - public: - QgsPointSample( QgsVectorLayer *inputLayer, const QString &outputLayer, const QString &nPointsAttribute, const QString &minDistAttribute = QString() ); - - /** Starts calculation of random points - \returns 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( QgsPointXY &pt, QgsSpatialIndex &index, double minDistance, QMap< QgsFeatureId, QgsPointXY > &pointMap ); - - //! Layer id of input polygon/multipolygon layer - QgsVectorLayer *mInputLayer = nullptr; - //! 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 diff --git a/tests/src/analysis/CMakeLists.txt b/tests/src/analysis/CMakeLists.txt index e79255854bd..21835c35c4f 100644 --- a/tests/src/analysis/CMakeLists.txt +++ b/tests/src/analysis/CMakeLists.txt @@ -63,7 +63,6 @@ ENDMACRO (ADD_QGIS_TEST) ############################################################# # Tests: SET(TESTS - testqgsvectoranalyzer.cpp testqgsgeometrysnapper.cpp testopenstreetmap.cpp testqgszonalstatistics.cpp diff --git a/tests/src/analysis/testqgsvectoranalyzer.cpp b/tests/src/analysis/testqgsvectoranalyzer.cpp deleted file mode 100644 index 057b210121b..00000000000 --- a/tests/src/analysis/testqgsvectoranalyzer.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/*************************************************************************** - testqgsvectoranalyzer.cpp - -------------------------------------- -Date : Sun Sep 16 12:22:49 AKDT 2007 -Copyright : (C) 2007 by Gary E. Sherman -Email : sherman at mrcc 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 "qgstest.h" - -//header for class being tested -#include -#include -#include -#include "qgsvectorlayer.h" - -class TestQgsVectorAnalyzer : public QObject -{ - Q_OBJECT - - public: - TestQgsVectorAnalyzer() - : mpLineLayer( 0 ) - , mpPolyLayer( 0 ) - , mpPointLayer( 0 ) - {} - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init() ;// will be called before each testfunction is executed. - void cleanup() ;// will be called after every testfunction. - //! Our tests proper begin here - void singleToMulti(); - void multiToSingle(); - void extractNodes(); - void polygonsToLines(); - void exportGeometryInfo(); - void simplifyGeometry(); - void polygonCentroids(); - void layerExtent(); - private: - QgsGeometryAnalyzer mAnalyzer; - QgsVectorLayer *mpLineLayer = nullptr; - QgsVectorLayer *mpPolyLayer = nullptr; - QgsVectorLayer *mpPointLayer = nullptr; - -}; - -void TestQgsVectorAnalyzer::initTestCase() -{ - // - // Runs once before any tests are run - // - // init QGIS's paths - true means that all path will be inited from prefix - QgsApplication::init(); - QgsApplication::initQgis(); - QgsApplication::showSettings(); - - //create some objects that will be used in all tests... - //create a map layer that will be used in all tests... - QString myBaseFileName( TEST_DATA_DIR ); //defined in CmakeLists.txt - QString myEndName = QStringLiteral( "lines.shp" ); - QString myFileName = myBaseFileName + '/' + myEndName; - qDebug() << myFileName; - QFileInfo myLineInfo( myFileName ); - mpLineLayer = new QgsVectorLayer( myLineInfo.filePath(), - myLineInfo.completeBaseName(), QStringLiteral( "ogr" ) ); - - myEndName = QStringLiteral( "polys.shp" ); - myFileName = myBaseFileName + '/' + myEndName; - QFileInfo myPolyInfo( myFileName ); - mpPolyLayer = new QgsVectorLayer( myPolyInfo.filePath(), - myPolyInfo.completeBaseName(), QStringLiteral( "ogr" ) ); - - myEndName = QStringLiteral( "points.shp" ); - myFileName = myBaseFileName + '/' + myEndName; - QFileInfo myPointInfo( myFileName ); - mpPointLayer = new QgsVectorLayer( myPointInfo.filePath(), - myPointInfo.completeBaseName(), QStringLiteral( "ogr" ) ); -} -void TestQgsVectorAnalyzer::cleanupTestCase() -{ - delete mpLineLayer; - delete mpPolyLayer; - delete mpPointLayer; - QgsApplication::exitQgis(); -} -void TestQgsVectorAnalyzer::init() -{ - -} -void TestQgsVectorAnalyzer::cleanup() -{ - -} -void TestQgsVectorAnalyzer::singleToMulti() -{ - -} -void TestQgsVectorAnalyzer::multiToSingle() -{ - -} -void TestQgsVectorAnalyzer::extractNodes() -{ - -} -void TestQgsVectorAnalyzer::polygonsToLines() -{ - -} -void TestQgsVectorAnalyzer::exportGeometryInfo() -{ -} - -void TestQgsVectorAnalyzer::simplifyGeometry() -{ - QString myTmpDir = QDir::tempPath() + '/'; - QString myFileName = myTmpDir + "simplify_layer.shp"; - QVERIFY( mAnalyzer.simplify( mpLineLayer, myFileName, 1.0 ) ); -} - -void TestQgsVectorAnalyzer::polygonCentroids() -{ - QString myTmpDir = QDir::tempPath() + '/'; - QString myFileName = myTmpDir + "centroid_layer.shp"; - QVERIFY( mAnalyzer.centroids( mpPolyLayer, myFileName ) ); -} - -void TestQgsVectorAnalyzer::layerExtent() -{ - QString myTmpDir = QDir::tempPath() + '/'; - QString myFileName = myTmpDir + "extent_layer.shp"; - QVERIFY( mAnalyzer.extent( mpPointLayer, myFileName ) ); -} - -QGSTEST_MAIN( TestQgsVectorAnalyzer ) -#include "testqgsvectoranalyzer.moc" From da6049ea38344451ab774f648ecd0d7a4e745ed6 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 15 Aug 2017 23:26:06 +0200 Subject: [PATCH 02/19] Adds delete layer context action to ogr and geopackage items TODO: decide what to do if the layer to be deleted was added to the canvas: block the deletion? Current implementation just ignores the fact with no apparent serious consequences beside that obviously the layer is not drawn anymore. --- src/providers/ogr/qgsgeopackagedataitems.cpp | 40 ++++++++++- src/providers/ogr/qgsgeopackagedataitems.h | 6 +- src/providers/ogr/qgsogrdataitems.cpp | 36 ++++++++++ src/providers/ogr/qgsogrdataitems.h | 6 ++ src/providers/ogr/qgsogrprovider.cpp | 73 ++++++++++++++++++++ 5 files changed, 159 insertions(+), 2 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index a51531334ad..5afad2f58ce 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -20,6 +20,7 @@ #include "qgsproject.h" #include "qgsvectorlayer.h" #include "qgsrasterlayer.h" +#include "qgsogrprovider.h" #include "qgsnewgeopackagelayerdialog.h" #include @@ -27,6 +28,8 @@ #include #include +QGISEXTERN bool deleteLayer( const QString &uri, const QString &errCause ); + QgsDataItem *QgsGeoPackageDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem ) { QgsDebugMsg( "path = " + path ); @@ -327,7 +330,6 @@ void QgsGeoPackageConnectionItem::addTable() QList QgsGeoPackageAbstractLayerItem::actions() { QList lst; - // TODO: delete layer when the provider supports it (not currently implemented) return lst; } @@ -352,3 +354,39 @@ QgsGeoPackageRasterLayerItem::QgsGeoPackageRasterLayerItem( QgsDataItem *parent, { } + + + +#ifdef HAVE_GUI +QList QgsGeoPackageVectorLayerItem::actions() +{ + QList lst = QgsGeoPackageAbstractLayerItem::actions(); + QAction *actionDeleteLayer = new QAction( tr( "Delete %1" ).arg( mName ), this ); + connect( actionDeleteLayer, &QAction::triggered, this, &QgsGeoPackageVectorLayerItem::deleteLayer ); + lst.append( actionDeleteLayer ); + return lst; +} + + +void QgsGeoPackageVectorLayerItem::deleteLayer() +{ + if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ), + QObject::tr( "Are you sure you want to delete %1?" ).arg( mName ), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes ) + return; + + QString errCause; + bool res = ::deleteLayer( mUri, errCause ); + if ( !res ) + { + QMessageBox::warning( nullptr, tr( "Delete Layer" ), errCause ); + } + else + { + QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer deleted successfully." ) ); + if ( mParent ) + mParent->refresh(); + } +} +#endif + diff --git a/src/providers/ogr/qgsgeopackagedataitems.h b/src/providers/ogr/qgsgeopackagedataitems.h index 3d47d4da921..8d52520e76e 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.h +++ b/src/providers/ogr/qgsgeopackagedataitems.h @@ -49,7 +49,11 @@ class QgsGeoPackageVectorLayerItem : public QgsGeoPackageAbstractLayerItem Q_OBJECT public: QgsGeoPackageVectorLayerItem( QgsDataItem *parent, QString name, QString path, QString uri, LayerType layerType ); - +#ifdef HAVE_GUI + QList actions() override; + public slots: + void deleteLayer(); +#endif }; diff --git a/src/providers/ogr/qgsogrdataitems.cpp b/src/providers/ogr/qgsogrdataitems.cpp index 2f92da08872..93edb89ed89 100644 --- a/src/providers/ogr/qgsogrdataitems.cpp +++ b/src/providers/ogr/qgsogrdataitems.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -31,6 +32,8 @@ QGISEXTERN QStringList fileExtensions(); QGISEXTERN QStringList wildcards(); +QGISEXTERN bool deleteLayer( const QString &uri, const QString &errCause ); + QgsOgrLayerItem::QgsOgrLayerItem( QgsDataItem *parent, QString name, QString path, QString uri, LayerType layerType ) @@ -56,6 +59,7 @@ QgsOgrLayerItem::QgsOgrLayerItem( QgsDataItem *parent, } } + bool QgsOgrLayerItem::setCrs( const QgsCoordinateReferenceSystem &crs ) { if ( !( mCapabilities & SetCrs ) ) @@ -110,6 +114,38 @@ QString QgsOgrLayerItem::layerName() const return info.completeBaseName(); } +#ifdef HAVE_GUI +QList QgsOgrLayerItem::actions() +{ + QList lst; + QAction *actionDeleteLayer = new QAction( tr( "Delete %1" ).arg( mName ), this ); + connect( actionDeleteLayer, &QAction::triggered, this, &QgsOgrLayerItem::deleteLayer ); + lst.append( actionDeleteLayer ); + return lst; +} + +void QgsOgrLayerItem::deleteLayer() +{ + if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ), + QObject::tr( "Are you sure you want to delete %1?" ).arg( mName ), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes ) + return; + + QString errCause; + bool res = ::deleteLayer( mUri, errCause ); + if ( !res ) + { + QMessageBox::warning( nullptr, tr( "Delete Layer" ), errCause ); + } + else + { + QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer deleted successfully." ) ); + if ( mParent ) + mParent->refresh(); + } +} +#endif + // ------- static QgsOgrLayerItem *dataItemForLayer( QgsDataItem *parentItem, QString name, QString path, OGRDataSourceH hDataSource, int layerId ) diff --git a/src/providers/ogr/qgsogrdataitems.h b/src/providers/ogr/qgsogrdataitems.h index a81ff1ba58d..cc88ebbea0c 100644 --- a/src/providers/ogr/qgsogrdataitems.h +++ b/src/providers/ogr/qgsogrdataitems.h @@ -29,6 +29,12 @@ class QgsOgrLayerItem : public QgsLayerItem bool setCrs( const QgsCoordinateReferenceSystem &crs ) override; QString layerName() const override; + +#ifdef HAVE_GUI + QList actions() override; + public slots: + void deleteLayer(); +#endif }; diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index e7dbdf088c1..505fc141140 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -4297,3 +4297,76 @@ QGISEXTERN void cleanupProvider() // NOTE: QgsApplication takes care of // calling OGRCleanupAll(); } + + + +QGISEXTERN bool deleteLayer( const QString &uri, QString &errCause ) +{ + bool isSubLayer; + int layerIndex; + QString layerName; + QString subsetString; + OGRwkbGeometryType ogrGeometryType; + QString filePath = AnalyzeURI( uri, + isSubLayer, + layerIndex, + layerName, + subsetString, + ogrGeometryType ); + + OGRDataSourceH hDS = GDALOpenEx( filePath.toLocal8Bit().data(), GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_UPDATE, NULL, NULL, NULL ); + if ( hDS && ( ! layerName.isEmpty() || layerIndex != -1 ) ) + { + if ( layerIndex == -1 ) + { + for ( int i = 0; i < GDALDatasetGetLayerCount( hDS ); i++ ) + { + OGRLayerH hL = GDALDatasetGetLayer( hDS, i ); + if ( layerName == QString( OGR_L_GetName( hL ) ) ) + { + layerIndex = i; + } + } + } + OGRErr error = GDALDatasetDeleteLayer( hDS, layerIndex ); + switch ( error ) + { + case OGRERR_NOT_ENOUGH_DATA: + errCause = QStringLiteral( "Not enough data to deserialize" ); + break; + case OGRERR_NOT_ENOUGH_MEMORY: + errCause = QStringLiteral( "Not enough memory" ); + break; + case OGRERR_UNSUPPORTED_GEOMETRY_TYPE: + errCause = QStringLiteral( "Unsupported geometry type" ); + break; + case OGRERR_UNSUPPORTED_OPERATION: + errCause = QStringLiteral( "Unsupported operation" ); + break; + case OGRERR_CORRUPT_DATA: + errCause = QStringLiteral( "Corrupt data" ); + break; + case OGRERR_FAILURE: + errCause = QStringLiteral( "Failure" ); + break; + case OGRERR_UNSUPPORTED_SRS: + errCause = QStringLiteral( "Unsupported SRS" ); + break; + case OGRERR_INVALID_HANDLE: + errCause = QStringLiteral( "Invalid handle" ); + break; + case OGRERR_NON_EXISTING_FEATURE: + errCause = QStringLiteral( "Non existing feature" ); + break; + default: + case OGRERR_NONE: + errCause = QStringLiteral( "Success" ); + break; + } + errCause = QStringLiteral( "OGR result code: %s" ).arg( errCause ); + return error == OGRERR_NONE; + } + // This should never happen: + errCause = QStringLiteral( "Layer not found: %s" ).arg( uri ); + return false; +} From 2ffa7ba3a189f5b6213804dd40cf7ff947b5776f Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 16 Aug 2017 08:56:50 +0200 Subject: [PATCH 03/19] Translate GDAL error codes --- src/providers/ogr/qgsogrprovider.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 505fc141140..c9f83520b7c 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -4332,41 +4332,41 @@ QGISEXTERN bool deleteLayer( const QString &uri, QString &errCause ) switch ( error ) { case OGRERR_NOT_ENOUGH_DATA: - errCause = QStringLiteral( "Not enough data to deserialize" ); + errCause = QObject::tr( "Not enough data to deserialize" ); break; case OGRERR_NOT_ENOUGH_MEMORY: - errCause = QStringLiteral( "Not enough memory" ); + errCause = QObject::tr( "Not enough memory" ); break; case OGRERR_UNSUPPORTED_GEOMETRY_TYPE: - errCause = QStringLiteral( "Unsupported geometry type" ); + errCause = QObject::tr( "Unsupported geometry type" ); break; case OGRERR_UNSUPPORTED_OPERATION: - errCause = QStringLiteral( "Unsupported operation" ); + errCause = QObject::tr( "Unsupported operation" ); break; case OGRERR_CORRUPT_DATA: - errCause = QStringLiteral( "Corrupt data" ); + errCause = QObject::tr( "Corrupt data" ); break; case OGRERR_FAILURE: - errCause = QStringLiteral( "Failure" ); + errCause = QObject::tr( "Failure" ); break; case OGRERR_UNSUPPORTED_SRS: - errCause = QStringLiteral( "Unsupported SRS" ); + errCause = QObject::tr( "Unsupported SRS" ); break; case OGRERR_INVALID_HANDLE: - errCause = QStringLiteral( "Invalid handle" ); + errCause = QObject::tr( "Invalid handle" ); break; case OGRERR_NON_EXISTING_FEATURE: - errCause = QStringLiteral( "Non existing feature" ); + errCause = QObject::tr( "Non existing feature" ); break; default: case OGRERR_NONE: - errCause = QStringLiteral( "Success" ); + errCause = QObject::tr( "Success" ); break; } - errCause = QStringLiteral( "OGR result code: %s" ).arg( errCause ); + errCause = QObject::tr( "GDAL result code: %s" ).arg( errCause ); return error == OGRERR_NONE; } // This should never happen: - errCause = QStringLiteral( "Layer not found: %s" ).arg( uri ); + errCause = QObject::tr( "Layer not found: %s" ).arg( uri ); return false; } From 162339d3d0709f0ecfcf8cf155d6999db90f512e Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 16 Aug 2017 10:12:03 +0200 Subject: [PATCH 04/19] Different messages for file and table layer deletion --- src/providers/ogr/qgsgeopackagedataitems.cpp | 4 +-- src/providers/ogr/qgsogrdataitems.cpp | 32 ++++++++++++++------ src/providers/ogr/qgsogrdataitems.h | 4 ++- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 5afad2f58ce..82a23a534da 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -361,7 +361,7 @@ QgsGeoPackageRasterLayerItem::QgsGeoPackageRasterLayerItem( QgsDataItem *parent, QList QgsGeoPackageVectorLayerItem::actions() { QList lst = QgsGeoPackageAbstractLayerItem::actions(); - QAction *actionDeleteLayer = new QAction( tr( "Delete %1" ).arg( mName ), this ); + QAction *actionDeleteLayer = new QAction( tr( "Delete layer '%1'..." ).arg( mName ), this ); connect( actionDeleteLayer, &QAction::triggered, this, &QgsGeoPackageVectorLayerItem::deleteLayer ); lst.append( actionDeleteLayer ); return lst; @@ -371,7 +371,7 @@ QList QgsGeoPackageVectorLayerItem::actions() void QgsGeoPackageVectorLayerItem::deleteLayer() { if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ), - QObject::tr( "Are you sure you want to delete %1?" ).arg( mName ), + QObject::tr( "Are you sure you want to delete layer '%1'?" ).arg( mName ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes ) return; diff --git a/src/providers/ogr/qgsogrdataitems.cpp b/src/providers/ogr/qgsogrdataitems.cpp index 93edb89ed89..4293384a0fe 100644 --- a/src/providers/ogr/qgsogrdataitems.cpp +++ b/src/providers/ogr/qgsogrdataitems.cpp @@ -36,9 +36,10 @@ QGISEXTERN bool deleteLayer( const QString &uri, const QString &errCause ); QgsOgrLayerItem::QgsOgrLayerItem( QgsDataItem *parent, - QString name, QString path, QString uri, LayerType layerType ) + QString name, QString path, QString uri, LayerType layerType, bool isSubLayer ) : QgsLayerItem( parent, name, path, uri, layerType, QStringLiteral( "ogr" ) ) { + mIsSubLayer = isSubLayer; mToolTip = uri; setState( Populated ); // children are not expected @@ -118,7 +119,9 @@ QString QgsOgrLayerItem::layerName() const QList QgsOgrLayerItem::actions() { QList lst; - QAction *actionDeleteLayer = new QAction( tr( "Delete %1" ).arg( mName ), this ); + // Messages are different for files and tables + QString message = mIsSubLayer ? QObject::tr( "Delete layer '%1'..." ).arg( mName ) : QObject::tr( "Delete file '%1'..." ).arg( mUri ); + QAction *actionDeleteLayer = new QAction( message, this ); connect( actionDeleteLayer, &QAction::triggered, this, &QgsOgrLayerItem::deleteLayer ); lst.append( actionDeleteLayer ); return lst; @@ -126,8 +129,19 @@ QList QgsOgrLayerItem::actions() void QgsOgrLayerItem::deleteLayer() { - if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ), - QObject::tr( "Are you sure you want to delete %1?" ).arg( mName ), + // Messages are different for files and tables + QString title = mIsSubLayer ? QObject::tr( "Delete Layer" ) : QObject::tr( "Delete File" ); + QString confirmMessage; + if ( mIsSubLayer ) + { + confirmMessage = QObject::tr( "Are you sure you want to delete layer '%1'?" ).arg( mName ); + } + else + { + confirmMessage = QObject::tr( "Are you sure you want to delete file '%1'?" ).arg( mUri ); + } + if ( QMessageBox::question( nullptr, title, + confirmMessage, QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes ) return; @@ -135,11 +149,11 @@ void QgsOgrLayerItem::deleteLayer() bool res = ::deleteLayer( mUri, errCause ); if ( !res ) { - QMessageBox::warning( nullptr, tr( "Delete Layer" ), errCause ); + QMessageBox::warning( nullptr, title, errCause ); } else { - QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer deleted successfully." ) ); + QMessageBox::information( nullptr, title, mIsSubLayer ? tr( "Layer deleted successfully." ) : tr( "File deleted successfully." ) ); if ( mParent ) mParent->refresh(); } @@ -148,7 +162,7 @@ void QgsOgrLayerItem::deleteLayer() // ------- -static QgsOgrLayerItem *dataItemForLayer( QgsDataItem *parentItem, QString name, QString path, OGRDataSourceH hDataSource, int layerId ) +static QgsOgrLayerItem *dataItemForLayer( QgsDataItem *parentItem, QString name, QString path, OGRDataSourceH hDataSource, int layerId, bool isSubLayer = false ) { OGRLayerH hLayer = OGR_DS_GetLayer( hDataSource, layerId ); OGRFeatureDefnH hDef = OGR_L_GetLayerDefn( hLayer ); @@ -202,7 +216,7 @@ static QgsOgrLayerItem *dataItemForLayer( QgsDataItem *parentItem, QString name, QgsDebugMsgLevel( "OGR layer uri : " + layerUri, 2 ); - return new QgsOgrLayerItem( parentItem, name, path, layerUri, layerType ); + return new QgsOgrLayerItem( parentItem, name, path, layerUri, layerType, isSubLayer ); } // ---- @@ -225,7 +239,7 @@ QVector QgsOgrDataCollectionItem::createChildren() children.reserve( numLayers ); for ( int i = 0; i < numLayers; ++i ) { - QgsOgrLayerItem *item = dataItemForLayer( this, QString(), mPath, hDataSource, i ); + QgsOgrLayerItem *item = dataItemForLayer( this, QString(), mPath, hDataSource, i, true ); children.append( item ); } diff --git a/src/providers/ogr/qgsogrdataitems.h b/src/providers/ogr/qgsogrdataitems.h index cc88ebbea0c..5edf1bdb0ea 100644 --- a/src/providers/ogr/qgsogrdataitems.h +++ b/src/providers/ogr/qgsogrdataitems.h @@ -24,7 +24,7 @@ class QgsOgrLayerItem : public QgsLayerItem { Q_OBJECT public: - QgsOgrLayerItem( QgsDataItem *parent, QString name, QString path, QString uri, LayerType layerType ); + QgsOgrLayerItem( QgsDataItem *parent, QString name, QString path, QString uri, LayerType layerType, bool isSubLayer = false ); bool setCrs( const QgsCoordinateReferenceSystem &crs ) override; @@ -35,6 +35,8 @@ class QgsOgrLayerItem : public QgsLayerItem public slots: void deleteLayer(); #endif + private: + bool mIsSubLayer; }; From cc1461909029526707f79d2689fb43eb9aefe32e Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 16 Aug 2017 14:10:10 +0200 Subject: [PATCH 05/19] Added 'datasource' and 'GeoPackage' to the delete confirm message --- src/providers/ogr/qgsgeopackagedataitems.cpp | 2 +- src/providers/ogr/qgsogrdataitems.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 82a23a534da..33560d5108c 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -371,7 +371,7 @@ QList QgsGeoPackageVectorLayerItem::actions() void QgsGeoPackageVectorLayerItem::deleteLayer() { if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ), - QObject::tr( "Are you sure you want to delete layer '%1'?" ).arg( mName ), + QObject::tr( "Are you sure you want to delete layer '%1' from GeoPackage?" ).arg( mName ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes ) return; diff --git a/src/providers/ogr/qgsogrdataitems.cpp b/src/providers/ogr/qgsogrdataitems.cpp index 4293384a0fe..964e73e793d 100644 --- a/src/providers/ogr/qgsogrdataitems.cpp +++ b/src/providers/ogr/qgsogrdataitems.cpp @@ -134,7 +134,7 @@ void QgsOgrLayerItem::deleteLayer() QString confirmMessage; if ( mIsSubLayer ) { - confirmMessage = QObject::tr( "Are you sure you want to delete layer '%1'?" ).arg( mName ); + confirmMessage = QObject::tr( "Are you sure you want to delete layer '%1' from datasource?" ).arg( mName ); } else { From 497d4a808f87a3495a178587aa3a8eb47f5e16f4 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 16 Aug 2017 14:50:29 +0200 Subject: [PATCH 06/19] Shows a warning and abort deletion if the layer is in the current project --- src/providers/ogr/qgsgeopackagedataitems.cpp | 42 ++++++++++----- src/providers/ogr/qgsogrdataitems.cpp | 54 +++++++++++++------- 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 33560d5108c..f7db7ac115e 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -356,7 +356,6 @@ QgsGeoPackageRasterLayerItem::QgsGeoPackageRasterLayerItem( QgsDataItem *parent, } - #ifdef HAVE_GUI QList QgsGeoPackageVectorLayerItem::actions() { @@ -370,22 +369,39 @@ QList QgsGeoPackageVectorLayerItem::actions() void QgsGeoPackageVectorLayerItem::deleteLayer() { - if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ), - QObject::tr( "Are you sure you want to delete layer '%1' from GeoPackage?" ).arg( mName ), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes ) - return; - - QString errCause; - bool res = ::deleteLayer( mUri, errCause ); - if ( !res ) + // Check if the layer is in the registry + const QgsMapLayer *projectLayer = nullptr; + Q_FOREACH ( const QgsMapLayer *layer, QgsProject::instance()->mapLayers() ) { - QMessageBox::warning( nullptr, tr( "Delete Layer" ), errCause ); + if ( layer->publicSource() == mUri ) + { + projectLayer = layer; + } + } + if ( ! projectLayer ) + { + if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ), + QObject::tr( "Are you sure you want to delete layer '%1' from GeoPackage?" ).arg( mName ), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes ) + return; + + QString errCause; + bool res = ::deleteLayer( mUri, errCause ); + if ( !res ) + { + QMessageBox::warning( nullptr, tr( "Delete Layer" ), errCause ); + } + else + { + QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer deleted successfully." ) ); + if ( mParent ) + mParent->refresh(); + } } else { - QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer deleted successfully." ) ); - if ( mParent ) - mParent->refresh(); + QMessageBox::warning( nullptr, QObject::tr( "Delete Layer" ), QObject::tr( "The layer '%1' cannot be deleted because it is in the current project as '%2'," + " remove it from the project and retry." ).arg( mName, projectLayer->name() ) ); } } #endif diff --git a/src/providers/ogr/qgsogrdataitems.cpp b/src/providers/ogr/qgsogrdataitems.cpp index 964e73e793d..6aef581b5c6 100644 --- a/src/providers/ogr/qgsogrdataitems.cpp +++ b/src/providers/ogr/qgsogrdataitems.cpp @@ -18,6 +18,7 @@ #include "qgslogger.h" #include "qgsmessagelog.h" #include "qgssettings.h" +#include "qgsproject.h" #include #include @@ -131,31 +132,48 @@ void QgsOgrLayerItem::deleteLayer() { // Messages are different for files and tables QString title = mIsSubLayer ? QObject::tr( "Delete Layer" ) : QObject::tr( "Delete File" ); - QString confirmMessage; - if ( mIsSubLayer ) + // Check if the layer is in the registry + const QgsMapLayer *projectLayer = nullptr; + Q_FOREACH ( const QgsMapLayer *layer, QgsProject::instance()->mapLayers() ) { - confirmMessage = QObject::tr( "Are you sure you want to delete layer '%1' from datasource?" ).arg( mName ); + if ( layer->publicSource() == mUri ) + { + projectLayer = layer; + } } - else + if ( ! projectLayer ) { - confirmMessage = QObject::tr( "Are you sure you want to delete file '%1'?" ).arg( mUri ); - } - if ( QMessageBox::question( nullptr, title, - confirmMessage, - QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes ) - return; + QString confirmMessage; + if ( mIsSubLayer ) + { + confirmMessage = QObject::tr( "Are you sure you want to delete layer '%1' from datasource?" ).arg( mName ); + } + else + { + confirmMessage = QObject::tr( "Are you sure you want to delete file '%1'?" ).arg( mUri ); + } + if ( QMessageBox::question( nullptr, title, + confirmMessage, + QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes ) + return; - QString errCause; - bool res = ::deleteLayer( mUri, errCause ); - if ( !res ) - { - QMessageBox::warning( nullptr, title, errCause ); + QString errCause; + bool res = ::deleteLayer( mUri, errCause ); + if ( !res ) + { + QMessageBox::warning( nullptr, title, errCause ); + } + else + { + QMessageBox::information( nullptr, title, mIsSubLayer ? tr( "Layer deleted successfully." ) : tr( "File deleted successfully." ) ); + if ( mParent ) + mParent->refresh(); + } } else { - QMessageBox::information( nullptr, title, mIsSubLayer ? tr( "Layer deleted successfully." ) : tr( "File deleted successfully." ) ); - if ( mParent ) - mParent->refresh(); + QMessageBox::warning( nullptr, title, QObject::tr( "The layer '%1' cannot be deleted because it is in the current project as '%2'," + " remove it from the project and retry." ).arg( mName, projectLayer->name() ) ); } } #endif From a5a4d3b7e1b6f2d0265c1c6dee32ca47b7fafa07 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 15 Aug 2017 21:29:47 +1000 Subject: [PATCH 07/19] Port gdal build vrt to new API --- .../algs/gdal/GdalAlgorithmProvider.py | 4 +- .../plugins/processing/algs/gdal/buildvrt.py | 55 +++++++++++-------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py b/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py index 2785aab2b93..28fa0e391c2 100644 --- a/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py +++ b/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py @@ -35,6 +35,7 @@ from .GdalUtils import GdalUtils from .AssignProjection import AssignProjection from .aspect import aspect +from .buildvrt import buildvrt from .ColorRelief import ColorRelief from .tri import tri from .warp import warp @@ -44,7 +45,6 @@ from .nearblack import nearblack # from .translate import translate # from .pct2rgb import pct2rgb # from .merge import merge -# from .buildvrt import buildvrt # from .polygonize import polygonize # from .gdaladdo import gdaladdo # from .ClipByExtent import ClipByExtent @@ -144,6 +144,7 @@ class GdalAlgorithmProvider(QgsProcessingProvider): # information(), AssignProjection(), aspect(), + buildvrt(), ColorRelief(), tri(), warp(), @@ -151,7 +152,6 @@ class GdalAlgorithmProvider(QgsProcessingProvider): # rgb2pct(), # pct2rgb(), # merge(), - # buildvrt(), # polygonize(), # gdaladdo(), # ClipByExtent(), diff --git a/python/plugins/processing/algs/gdal/buildvrt.py b/python/plugins/processing/algs/gdal/buildvrt.py index 71fbc272db2..cc543df7b75 100644 --- a/python/plugins/processing/algs/gdal/buildvrt.py +++ b/python/plugins/processing/algs/gdal/buildvrt.py @@ -29,14 +29,16 @@ import os from qgis.PyQt.QtGui import QIcon -from qgis.core import QgsProcessingUtils +from qgis.core import (QgsProcessing, + QgsProperty, + QgsProcessingParameterMultipleLayers, + QgsProcessingParameterEnum, + QgsProcessingParameterBoolean, + QgsProcessingParameterRasterDestination, + QgsProcessingOutputLayerDefinition, + QgsProcessingUtils) from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm -from processing.core.outputs import OutputRaster -from processing.core.parameters import ParameterBoolean -from processing.core.parameters import ParameterMultipleInput -from processing.core.parameters import ParameterSelection from processing.algs.gdal.GdalUtils import GdalUtils -from processing.tools import dataobjects pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] @@ -55,15 +57,15 @@ class buildvrt(GdalAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterMultipleInput(self.INPUT, - self.tr('Input layers'), dataobjects.TYPE_RASTER)) - self.addParameter(ParameterSelection(self.RESOLUTION, - self.tr('Resolution'), self.RESOLUTION_OPTIONS, 0)) - self.addParameter(ParameterBoolean(self.SEPARATE, - self.tr('Layer stack'), True)) - self.addParameter(ParameterBoolean(self.PROJ_DIFFERENCE, - self.tr('Allow projection difference'), False)) - self.addOutput(OutputRaster(buildvrt.OUTPUT, self.tr('Virtual'))) + self.addParameter(QgsProcessingParameterMultipleLayers(self.INPUT, + self.tr('Input layers'), QgsProcessing.TypeRaster)) + self.addParameter(QgsProcessingParameterEnum(self.RESOLUTION, + self.tr('Resolution'), options=self.RESOLUTION_OPTIONS, defaultValue=0)) + self.addParameter(QgsProcessingParameterBoolean(self.SEPARATE, + self.tr('Layer stack'), defaultValue=True)) + self.addParameter(QgsProcessingParameterBoolean(self.PROJ_DIFFERENCE, + self.tr('Allow projection difference'), defaultValue=False)) + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Virtual'))) def name(self): return 'buildvirtualraster' @@ -80,25 +82,34 @@ class buildvrt(GdalAlgorithm): def getConsoleCommands(self, parameters, context, feedback): arguments = [] arguments.append('-resolution') - arguments.append(self.RESOLUTION_OPTIONS[self.getParameterValue(self.RESOLUTION)]) - if self.getParameterValue(buildvrt.SEPARATE): + arguments.append(self.RESOLUTION_OPTIONS[self.parameterAsEnum(parameters, self.RESOLUTION, context)]) + if self.parameterAsBool(parameters, buildvrt.SEPARATE, context): arguments.append('-separate') - if self.getParameterValue(buildvrt.PROJ_DIFFERENCE): + if self.parameterAsBool(parameters, buildvrt.PROJ_DIFFERENCE, context): arguments.append('-allow_projection_difference') # Always write input files to a text file in case there are many of them and the # length of the command will be longer then allowed in command prompt listFile = os.path.join(QgsProcessingUtils.tempFolder(), 'buildvrtInputFiles.txt') with open(listFile, 'w') as f: - f.write(self.getParameterValue(buildvrt.INPUT).replace(';', '\n')) + layers = [] + for l in self.parameterAsLayerList(parameters, self.INPUT, context): + layers.append(l.source()) + f.write('\n'.join(layers)) arguments.append('-input_file_list') arguments.append(listFile) - out = self.getOutputValue(buildvrt.OUTPUT) + + out = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) # Ideally the file extensions should be limited to just .vrt but I'm not sure how # to do it simply so instead a check is performed. _, ext = os.path.splitext(out) if not ext.lower() == '.vrt': - out = out.replace(ext, '.vrt') - self.setOutputValue(self.OUTPUT, out) + out = out[:-len(ext)] + '.vrt' + if isinstance(parameters[self.OUTPUT], QgsProcessingOutputLayerDefinition): + output_def = QgsProcessingOutputLayerDefinition(parameters[self.OUTPUT]) + output_def.sink = QgsProperty.fromValue(out) + self.setOutputValue(self.OUTPUT, output_def) + else: + self.setOutputValue(self.OUTPUT, out) arguments.append(out) return ['gdalbuildvrt', GdalUtils.escapeAndJoin(arguments)] From 1f2ea024f274590b1921d43c10eae48635f2673f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 15 Aug 2017 22:06:22 +1000 Subject: [PATCH 08/19] Allow adding layers which aren't open in the project to processing parameters which accept lists of multiple layers E.g. build vrt alg, merge vector layers alg Otherwise you may need to load 100's of layers temporarily into a project to perform algs on them --- .../processing/gui/MultipleInputDialog.py | 66 +++++++++++++++++-- .../processing/gui/MultipleInputPanel.py | 24 ++++--- python/plugins/processing/gui/wrappers.py | 6 +- 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/python/plugins/processing/gui/MultipleInputDialog.py b/python/plugins/processing/gui/MultipleInputDialog.py index a8fd860f02d..47f4c6e337d 100644 --- a/python/plugins/processing/gui/MultipleInputDialog.py +++ b/python/plugins/processing/gui/MultipleInputDialog.py @@ -29,12 +29,16 @@ __revision__ = '$Format:%H$' import os -from qgis.core import QgsSettings +from qgis.core import (QgsSettings, + QgsProcessing, + QgsVectorFileWriter, + QgsProviderRegistry) from qgis.PyQt import uic from qgis.PyQt.QtCore import Qt from qgis.PyQt.QtCore import QByteArray -from qgis.PyQt.QtWidgets import QDialog, QAbstractItemView, QPushButton, QDialogButtonBox +from qgis.PyQt.QtWidgets import QDialog, QAbstractItemView, QPushButton, QDialogButtonBox, QFileDialog from qgis.PyQt.QtGui import QStandardItemModel, QStandardItem +from processing.tools import dataobjects pluginPath = os.path.split(os.path.dirname(__file__))[0] WIDGET, BASE = uic.loadUiType( @@ -43,9 +47,11 @@ WIDGET, BASE = uic.loadUiType( class MultipleInputDialog(BASE, WIDGET): - def __init__(self, options, selectedoptions=None): + def __init__(self, options, selectedoptions=None, datatype=None): super(MultipleInputDialog, self).__init__(None) self.setupUi(self) + self.datatype = datatype + self.model = None self.lstLayers.setSelectionMode(QAbstractItemView.NoSelection) @@ -68,6 +74,11 @@ class MultipleInputDialog(BASE, WIDGET): self.btnToggleSelection = QPushButton(self.tr('Toggle selection')) self.buttonBox.addButton(self.btnToggleSelection, QDialogButtonBox.ActionRole) + if self.datatype is not None: + btnAddFile = QPushButton(self.tr('Add file(s)…')) + btnAddFile.clicked.connect(self.addFiles) + self.buttonBox.addButton(btnAddFile, + QDialogButtonBox.ActionRole) self.btnSelectAll.clicked.connect(lambda: self.selectAll(True)) self.btnClearSelection.clicked.connect(lambda: self.selectAll(False)) @@ -83,15 +94,23 @@ class MultipleInputDialog(BASE, WIDGET): self.settings.setValue("/Processing/multipleInputDialogGeometry", self.saveGeometry()) def populateList(self): - model = QStandardItemModel() + self.model = QStandardItemModel() for value, text in self.options: item = QStandardItem(text) item.setData(value, Qt.UserRole) item.setCheckState(Qt.Checked if value in self.selectedoptions else Qt.Unchecked) item.setCheckable(True) - model.appendRow(item) + self.model.appendRow(item) - self.lstLayers.setModel(model) + # add extra options (e.g. manually added layers) + for t in [o for o in self.selectedoptions if not isinstance(o, int)]: + item = QStandardItem(t) + item.setData(t, Qt.UserRole) + item.setCheckState(Qt.Checked) + item.setCheckable(True) + self.model.appendRow(item) + + self.lstLayers.setModel(self.model) def accept(self): self.selectedoptions = [] @@ -118,3 +137,38 @@ class MultipleInputDialog(BASE, WIDGET): item = model.item(i) checked = item.checkState() == Qt.Checked item.setCheckState(Qt.Unchecked if checked else Qt.Checked) + + def getFileFilter(self, datatype): + """ + Returns a suitable file filter pattern for the specified parameter definition + :param param: + :return: + """ + if datatype == QgsProcessing.TypeRaster: + return QgsProviderRegistry.instance().fileRasterFilters() + elif datatype == QgsProcessing.TypeFile: + return self.tr('All files (*.*)') + else: + exts = QgsVectorFileWriter.supportedFormatExtensions() + for i in range(len(exts)): + exts[i] = self.tr('{0} files (*.{1})').format(exts[i].upper(), exts[i].lower()) + return self.tr('All files (*.*)') + ';;' + ';;'.join(exts) + + def addFiles(self): + filter = self.getFileFilter(self.datatype) + + settings = QgsSettings() + path = str(settings.value('/Processing/LastInputPath')) + + ret, selected_filter = QFileDialog.getOpenFileNames(self, self.tr('Select file(s)'), + path, filter) + if ret: + files = list(ret) + settings.setValue('/Processing/LastInputPath', + os.path.dirname(str(files[0]))) + for filename in files: + item = QStandardItem(filename) + item.setData(filename, Qt.UserRole) + item.setCheckState(Qt.Checked) + item.setCheckable(True) + self.model.appendRow(item) diff --git a/python/plugins/processing/gui/MultipleInputPanel.py b/python/plugins/processing/gui/MultipleInputPanel.py index c33a6a6535f..7ac7e85274f 100644 --- a/python/plugins/processing/gui/MultipleInputPanel.py +++ b/python/plugins/processing/gui/MultipleInputPanel.py @@ -28,9 +28,10 @@ __revision__ = '$Format:%H$' import os +from qgis.core import QgsProcessing from qgis.PyQt import uic from qgis.PyQt.QtCore import pyqtSignal - +'' from processing.gui.MultipleInputDialog import MultipleInputDialog from processing.gui.MultipleFileInputDialog import MultipleFileInputDialog @@ -63,10 +64,10 @@ class MultipleInputPanel(BASE, WIDGET): self.tr('{0} elements selected').format(len(self.selectedoptions))) def showSelectionDialog(self): - if self.datatype is None: - dlg = MultipleInputDialog(self.options, self.selectedoptions) - else: + if self.datatype == QgsProcessing.TypeFile: dlg = MultipleFileInputDialog(self.selectedoptions) + else: + dlg = MultipleInputDialog(self.options, self.selectedoptions, datatype=self.datatype) dlg.exec_() if dlg.selectedoptions is not None: self.selectedoptions = dlg.selectedoptions @@ -76,12 +77,15 @@ class MultipleInputPanel(BASE, WIDGET): def updateForOptions(self, options): selectedoptions = [] - selected = [self.options[i] for i in self.selectedoptions] + selected = [self.options[i] if isinstance(i, int) else i for i in self.selectedoptions] for sel in selected: - try: - idx = options.index(sel) - selectedoptions.append(idx) - except ValueError: - pass + if isinstance(sel, int): + try: + idx = options.index(sel) + selectedoptions.append(idx) + except ValueError: + pass + else: + selectedoptions.append(sel) self.options = options self.setSelectedItems(selectedoptions) diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index 9f8eecc13f9..307e6b22910 100644 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -546,7 +546,7 @@ class MultipleInputWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType == DIALOG_STANDARD: if self.param.layerType() == QgsProcessing.TypeFile: - return MultipleInputPanel(datatype=dataobjects.TYPE_FILE) + return MultipleInputPanel(datatype=QgsProcessing.TypeFile) else: if self.param.layerType() == QgsProcessing.TypeRaster: options = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False) @@ -555,7 +555,7 @@ class MultipleInputWidgetWrapper(WidgetWrapper): else: options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.param.layerType()], False) opts = [getExtendedLayerName(opt) for opt in options] - return MultipleInputPanel(opts) + return MultipleInputPanel(opts, datatype=self.param.layerType()) elif self.dialogType == DIALOG_BATCH: widget = BatchInputSelectionPanel(self.param, self.row, self.col, self.dialog) widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) @@ -599,7 +599,7 @@ class MultipleInputWidgetWrapper(WidgetWrapper): options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False) else: options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.param.layerType()], False) - return [options[i] for i in self.widget.selectedoptions] + return [options[i] if isinstance(i, int) else i for i in self.widget.selectedoptions] elif self.dialogType == DIALOG_BATCH: return self.widget.getText() else: From 0a7bb4869b0df3d2bea8db3aef78258163b7b841 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 15 Aug 2017 22:22:55 +1000 Subject: [PATCH 09/19] Allow selecting multiple items in processing multi layer input dialog And selecting/deselecting only these items. Otherwise it can be quite cumbersome to manually select complex sets of items (e.g. try needing to select ~50% of the layers in a large project - there's currently no quick way to do this.) With this change you can at least ctrl/shift click to create selections quicker. --- .../processing/gui/MultipleInputDialog.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/python/plugins/processing/gui/MultipleInputDialog.py b/python/plugins/processing/gui/MultipleInputDialog.py index 47f4c6e337d..d29734a1d71 100644 --- a/python/plugins/processing/gui/MultipleInputDialog.py +++ b/python/plugins/processing/gui/MultipleInputDialog.py @@ -87,6 +87,7 @@ class MultipleInputDialog(BASE, WIDGET): self.settings = QgsSettings() self.restoreGeometry(self.settings.value("/Processing/multipleInputDialogGeometry", QByteArray())) + self.lstLayers.setSelectionMode(QAbstractItemView.ExtendedSelection) self.populateList() self.finished.connect(self.saveWindowGeometry) @@ -125,16 +126,22 @@ class MultipleInputDialog(BASE, WIDGET): self.selectedoptions = None QDialog.reject(self) + def getItemsToModify(self): + items = [] + if len(self.lstLayers.selectedIndexes()) > 1: + for i in self.lstLayers.selectedIndexes(): + items.append(self.model.itemFromIndex(i)) + else: + for i in range(self.model.rowCount()): + items.append(self.model.item(i)) + return items + def selectAll(self, value): - model = self.lstLayers.model() - for i in range(model.rowCount()): - item = model.item(i) + for item in self.getItemsToModify(): item.setCheckState(Qt.Checked if value else Qt.Unchecked) def toggleSelection(self): - model = self.lstLayers.model() - for i in range(model.rowCount()): - item = model.item(i) + for item in self.getItemsToModify(): checked = item.checkState() == Qt.Checked item.setCheckState(Qt.Unchecked if checked else Qt.Checked) From 48eccc379de45539adba9ae5064355adb90a4746 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 04:11:53 +1000 Subject: [PATCH 10/19] Default to vrt extension for build vrt alg output --- python/plugins/processing/algs/gdal/buildvrt.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/python/plugins/processing/algs/gdal/buildvrt.py b/python/plugins/processing/algs/gdal/buildvrt.py index cc543df7b75..07d9afe2fd3 100644 --- a/python/plugins/processing/algs/gdal/buildvrt.py +++ b/python/plugins/processing/algs/gdal/buildvrt.py @@ -57,6 +57,18 @@ class buildvrt(GdalAlgorithm): super().__init__() def initAlgorithm(self, config=None): + + class ParameterVrtDestination(QgsProcessingParameterRasterDestination): + + def __init__(self, name, description): + super().__init__(name, description) + + def type(self): + return 'vrt_destination' + + def defaultFileExtension(self): + return 'vrt' + self.addParameter(QgsProcessingParameterMultipleLayers(self.INPUT, self.tr('Input layers'), QgsProcessing.TypeRaster)) self.addParameter(QgsProcessingParameterEnum(self.RESOLUTION, @@ -65,7 +77,7 @@ class buildvrt(GdalAlgorithm): self.tr('Layer stack'), defaultValue=True)) self.addParameter(QgsProcessingParameterBoolean(self.PROJ_DIFFERENCE, self.tr('Allow projection difference'), defaultValue=False)) - self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Virtual'))) + self.addParameter(ParameterVrtDestination(self.OUTPUT, self.tr('Virtual'))) def name(self): return 'buildvirtualraster' From e6814bca2c9a21df814e30b78ecfa4af42539246 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 16 Aug 2017 20:23:21 +0200 Subject: [PATCH 11/19] [feature] Geopackage handle drops in the browser It is now possible to drag and drop from any vector layer onto a gpkg node layer and import a layer. Overwrites are protected by a confirmation dialog. --- src/providers/ogr/qgsgeopackagedataitems.cpp | 111 +++++++++++++++++++ src/providers/ogr/qgsgeopackagedataitems.h | 2 +- 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index a51531334ad..49d7b837350 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -21,6 +21,8 @@ #include "qgsvectorlayer.h" #include "qgsrasterlayer.h" #include "qgsnewgeopackagelayerdialog.h" +#include "qgsmessageoutput.h" +#include "qgsvectorlayerexporter.h" #include #include @@ -268,6 +270,115 @@ QList QgsGeoPackageConnectionItem::actions() } #endif + + +bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAction ) +{ + + if ( !QgsMimeDataUtils::isUriList( data ) ) + return false; + + // TODO: probably should show a GUI with settings etc + QString uri; + + QStringList importResults; + bool hasError = false; + + QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( data ); + Q_FOREACH ( const QgsMimeDataUtils::Uri &u, lst ) + { + if ( u.layerType == QStringLiteral( "vector" ) ) + { + // open the source layer + bool owner; + QString error; + QgsVectorLayer *srcLayer = u.vectorLayer( owner, error ); + if ( !srcLayer ) + { + importResults.append( tr( "%1: %2" ).arg( u.name ).arg( error ) ); + hasError = true; + continue; + } + + if ( srcLayer->isValid() ) + { + uri = mPath; + QgsDebugMsgLevel( "URI " + uri, 3 ); + + // check if the destination layer already exists + bool exists = false; + // Q_FOREACH won't detach ... + for ( const auto child : children() ) + { + if ( child->name() == u.name ) + { + exists = true; + } + } + if ( ! exists || QMessageBox::question( nullptr, tr( "Overwrite Layer" ), + tr( "Destination layer %1 already exists. Do you want to overwrite it?" ).arg( u.name ), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::Yes ) + { + + std::unique_ptr< QMap > options( new QMap ); + options->insert( QStringLiteral( "driverName" ), QStringLiteral( "GPKG" ) ); + options->insert( QStringLiteral( "update" ), true ); + options->insert( QStringLiteral( "overwrite" ), true ); + options->insert( QStringLiteral( "layerName" ), u.name ); + + std::unique_ptr< QgsVectorLayerExporterTask > exportTask( new QgsVectorLayerExporterTask( srcLayer, uri, QStringLiteral( "ogr" ), srcLayer->crs(), options.get(), owner ) ); + + // when export is successful: + connect( exportTask.get(), &QgsVectorLayerExporterTask::exportComplete, this, [ = ]() + { + // this is gross - TODO - find a way to get access to messageBar from data items + QMessageBox::information( nullptr, tr( "Import to GeoPackage database" ), tr( "Import was successful." ) ); + refreshConnections(); + } ); + + // when an error occurs: + connect( exportTask.get(), &QgsVectorLayerExporterTask::errorOccurred, this, [ = ]( int error, const QString & errorMessage ) + { + if ( error != QgsVectorLayerExporter::ErrUserCanceled ) + { + QgsMessageOutput *output = QgsMessageOutput::createMessageOutput(); + output->setTitle( tr( "Import to GeoPackage database" ) ); + output->setMessage( tr( "Failed to import some layers!\n\n" ) + errorMessage, QgsMessageOutput::MessageText ); + output->showMessage(); + } + } ); + + QgsApplication::taskManager()->addTask( exportTask.release() ); + } + } + else + { + importResults.append( tr( "%1: Not a valid layer!" ).arg( u.name ) ); + hasError = true; + } + } + else + { + // TODO: implemnent raster import + QgsMessageOutput *output = QgsMessageOutput::createMessageOutput(); + output->setTitle( tr( "Import to GeoPackage database faile" ) ); + output->setMessage( tr( "Failed to import some layers!\n\n" ) + QStringLiteral( "Raster import is not yet implemented!\n" ), QgsMessageOutput::MessageText ); + output->showMessage(); + } + + } + + if ( hasError ) + { + QgsMessageOutput *output = QgsMessageOutput::createMessageOutput(); + output->setTitle( tr( "Import to GeoPackage database" ) ); + output->setMessage( tr( "Failed to import some layers!\n\n" ) + importResults.join( QStringLiteral( "\n" ) ), QgsMessageOutput::MessageText ); + output->showMessage(); + } + + return true; +} + + QgsLayerItem::LayerType QgsGeoPackageConnectionItem::layerTypeFromDb( const QString &geometryType ) { if ( geometryType.contains( QStringLiteral( "Point" ), Qt::CaseInsensitive ) ) diff --git a/src/providers/ogr/qgsgeopackagedataitems.h b/src/providers/ogr/qgsgeopackagedataitems.h index 3d47d4da921..b16c31d5f07 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.h +++ b/src/providers/ogr/qgsgeopackagedataitems.h @@ -67,7 +67,7 @@ class QgsGeoPackageConnectionItem : public QgsDataCollectionItem #endif virtual bool acceptDrop() override { return true; } - //virtual bool handleDrop( const QMimeData *data, Qt::DropAction action ) override; + virtual bool handleDrop( const QMimeData *data, Qt::DropAction action ) override; //! Return the layer type from \a geometryType static QgsLayerItem::LayerType layerTypeFromDb( const QString &geometryType ); From e1b0f7861922e4709566f62adc8a7c9b916cec51 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 04:38:36 +1000 Subject: [PATCH 12/19] Fix default layer sometimes inserted into multi layer parameter values --- src/core/processing/qgsprocessingparameters.cpp | 2 +- tests/src/core/testqgsprocessing.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index db83a397888..0e7574b2afb 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -622,7 +622,7 @@ QList QgsProcessingParameters::parameterAsLayerList( const QgsPro else resultStringList << val.toString(); - if ( ( resultStringList.isEmpty() || resultStringList.at( 0 ).isEmpty() ) ) + if ( layers.isEmpty() && ( resultStringList.isEmpty() || resultStringList.at( 0 ).isEmpty() ) ) { resultStringList.clear(); // check default diff --git a/tests/src/core/testqgsprocessing.cpp b/tests/src/core/testqgsprocessing.cpp index 10915eaf1a1..6055079e74a 100644 --- a/tests/src/core/testqgsprocessing.cpp +++ b/tests/src/core/testqgsprocessing.cpp @@ -2310,6 +2310,9 @@ void TestQgsProcessing::parameterLayerList() params.insert( "optional", QVariant() ); QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 ); + params.insert( "optional", QVariantList() << QVariant::fromValue( r1 ) ); + QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << r1 ); + code = def->asScriptCode(); QCOMPARE( code, QStringLiteral( "##optional=optional multiple vector " ) + v1->id() ); fromCode.reset( dynamic_cast< QgsProcessingParameterMultipleLayers * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) ); From c54667c3b142ff156194e70bd2a6d0df8890664e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 05:00:47 +1000 Subject: [PATCH 13/19] Handle mixed lists of values and lists when evaluating multilayer parameters This can happen when running models with mixed input types for multilayer parameters --- .../processing/qgsprocessingparameters.cpp | 42 +++++++++++-------- tests/src/core/testqgsprocessing.cpp | 11 ++++- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index 0e7574b2afb..af1d1271667 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -22,6 +22,7 @@ #include "qgsprocessingoutputs.h" #include "qgssettings.h" #include "qgsvectorfilewriter.h" +#include bool QgsProcessingParameters::isDynamic( const QVariantMap ¶meters, const QString &name ) { @@ -596,31 +597,36 @@ QList QgsProcessingParameters::parameterAsLayerList( const QgsPro QStringList resultStringList; - if ( val.canConvert() ) - resultStringList << val.value< QgsProperty >().valueAsString( context.expressionContext(), definition->defaultValue().toString() ); - else if ( val.type() == QVariant::List ) + std::function< void( const QVariant &var ) > processVariant; + processVariant = [ &resultStringList, &layers, &context, &definition, &processVariant ]( const QVariant & var ) { - Q_FOREACH ( const QVariant &var, val.toList() ) + if ( var.type() == QVariant::List ) { - if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( qvariant_cast( var ) ) ) + Q_FOREACH ( const QVariant &listVar, var.toList() ) { - layers << layer; - } - else - { - resultStringList << var.toString(); + processVariant( listVar ); } } - } - else if ( val.type() == QVariant::StringList ) - { - Q_FOREACH ( const QString &s, val.toStringList() ) + else if ( var.type() == QVariant::StringList ) { - resultStringList << s; + Q_FOREACH ( const QString &s, var.toStringList() ) + { + resultStringList << s; + } } - } - else - resultStringList << val.toString(); + else if ( var.canConvert() ) + resultStringList << var.value< QgsProperty >().valueAsString( context.expressionContext(), definition->defaultValue().toString() ); + else if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( qvariant_cast( var ) ) ) + { + layers << layer; + } + else + { + resultStringList << var.toString(); + } + }; + + processVariant( val ); if ( layers.isEmpty() && ( resultStringList.isEmpty() || resultStringList.at( 0 ).isEmpty() ) ) { diff --git a/tests/src/core/testqgsprocessing.cpp b/tests/src/core/testqgsprocessing.cpp index 6055079e74a..87ff8bcb6b0 100644 --- a/tests/src/core/testqgsprocessing.cpp +++ b/tests/src/core/testqgsprocessing.cpp @@ -2183,7 +2183,8 @@ void TestQgsProcessing::parameterLayerList() QFileInfo fi1( raster1 ); QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" ); QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon?crs=EPSG:3111", "V4", "memory" ); - p.addMapLayers( QList() << v1 << r1 ); + QgsVectorLayer *v2 = new QgsVectorLayer( "Polygon?crs=EPSG:3111", "V5", "memory" ); + p.addMapLayers( QList() << v1 << v2 << r1 ); QgsProcessingContext context; context.setProject( &p ); @@ -2225,6 +2226,14 @@ void TestQgsProcessing::parameterLayerList() params.insert( "non_optional", QVariantList() << QVariant::fromValue( v1 ) << QVariant::fromValue( r1 ) ); QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << r1 ); + // mix of list and single layers (happens from models) + params.insert( "non_optional", QVariantList() << QVariant( QVariantList() << QVariant::fromValue( v1 ) << QVariant::fromValue( v2 ) ) << QVariant::fromValue( r1 ) ); + QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << v2 << r1 ); + + // mix of two lists (happens from models) + params.insert( "non_optional", QVariantList() << QVariant( QVariantList() << QVariant::fromValue( v1 ) << QVariant::fromValue( v2 ) ) << QVariant( QVariantList() << QVariant::fromValue( r1 ) ) ); + QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << v2 << r1 ); + // mix of existing layers and non project layer string params.insert( "non_optional", QVariantList() << v1->id() << raster2 ); QList< QgsMapLayer *> layers = QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ); From b318aff6ff4ee762e5e44e04709e8155c16bdebe Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 05:12:11 +1000 Subject: [PATCH 14/19] Fix multi layer parameters in models not giving all options for inputs --- python/plugins/processing/gui/wrappers.py | 12 ++++++------ .../processing/modeler/ModelerParametersDialog.py | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index 307e6b22910..6d3580bf0a3 100644 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -524,20 +524,20 @@ class MultipleInputWidgetWrapper(WidgetWrapper): def _getOptions(self): if self.param.layerType() == QgsProcessing.TypeVectorAny: - options = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFeatureSource, QgsProcessingOutputVectorLayer) + options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), QgsProcessingOutputVectorLayer) elif self.param.layerType() == QgsProcessing.TypeVectorPoint: - options = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFeatureSource, QgsProcessingOutputVectorLayer, + options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), QgsProcessingOutputVectorLayer, [QgsProcessing.TypeVectorPoint, QgsProcessing.TypeVectorAny]) elif self.param.layerType() == QgsProcessing.TypeVectorLine: - options = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFeatureSource, QgsProcessingOutputVectorLayer, + options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), QgsProcessingOutputVectorLayer, [QgsProcessing.TypeVectorLine, QgsProcessing.TypeVectorAny]) elif self.param.layerType() == QgsProcessing.TypeVectorPolygon: - options = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFeatureSource, QgsProcessingOutputVectorLayer, + options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), QgsProcessingOutputVectorLayer, [QgsProcessing.TypeVectorPolygon, QgsProcessing.TypeVectorAny]) elif self.param.layerType() == QgsProcessing.TypeRaster: - options = self.dialog.getAvailableValuesOfType(QgsProcessingParameterRasterLayer, QgsProcessingOutputRasterLayer) + options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterRasterLayer, QgsProcessingParameterMultipleLayers), QgsProcessingOutputRasterLayer) elif self.param.layerType() == QgsProcessing.TypeTable: - options = self.dialog.getAvailableValuesOfType(QgsProcessingParameterVectorLayer, OutputTable) + options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), OutputTable) else: options = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFile, OutputFile) options = sorted(options, key=lambda opt: self.dialog.resolveValueDescription(opt)) diff --git a/python/plugins/processing/modeler/ModelerParametersDialog.py b/python/plugins/processing/modeler/ModelerParametersDialog.py index 08d29209b43..536b20ac79c 100644 --- a/python/plugins/processing/modeler/ModelerParametersDialog.py +++ b/python/plugins/processing/modeler/ModelerParametersDialog.py @@ -44,6 +44,7 @@ from qgis.core import (QgsProcessingParameterDefinition, QgsProcessingModelChildAlgorithm, QgsProcessingModelChildParameterSource, QgsProcessingParameterFeatureSink, + QgsProcessingParameterMultipleLayers, QgsProcessingParameterRasterDestination, QgsProcessingParameterFileDestination, QgsProcessingParameterFolderDestination, @@ -236,11 +237,11 @@ class ModelerParametersDialog(QDialog): # upgrade paramType to list if paramType is None: paramType = [] - elif not isinstance(paramType, list): + elif not isinstance(paramType, (tuple, list)): paramType = [paramType] if outTypes is None: outTypes = [] - elif not isinstance(outTypes, list): + elif not isinstance(outTypes, (tuple, list)): outTypes = [outTypes] return self.model.availableSourcesForChild(self.childId, [p.typeName() for p in paramType if issubclass(p, QgsProcessingParameterDefinition)], From aefd5cce30974a0b32733d2ba20d3c3e571899a3 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 05:18:33 +1000 Subject: [PATCH 15/19] Fix exception when editing multi layer parameter in model --- python/plugins/processing/gui/wrappers.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index 6d3580bf0a3..f38174935aa 100644 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -584,8 +584,12 @@ class MultipleInputWidgetWrapper(WidgetWrapper): options = self._getOptions() selected = [] for i, opt in enumerate(options): - if opt in value: - selected.append(i) + try: + if opt in value: + selected.append(i) + except TypeError: + if opt == value: + selected.append(i) self.widget.setSelectedItems(selected) def value(self): From 813978660536bbb5663afbb123eef7c36afe41d6 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 06:00:28 +1000 Subject: [PATCH 16/19] Allow adding manual layers in modeler multi layer alg parameters This allows creation of models with child algorithms which do things like merge a selected layer with a predefined static layer, or create a vrt with a mix of static and user selected layers. --- .../processing/gui/MultipleInputDialog.py | 10 +++++-- python/plugins/processing/gui/wrappers.py | 28 +++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/python/plugins/processing/gui/MultipleInputDialog.py b/python/plugins/processing/gui/MultipleInputDialog.py index d29734a1d71..222f9e556fe 100644 --- a/python/plugins/processing/gui/MultipleInputDialog.py +++ b/python/plugins/processing/gui/MultipleInputDialog.py @@ -32,7 +32,8 @@ import os from qgis.core import (QgsSettings, QgsProcessing, QgsVectorFileWriter, - QgsProviderRegistry) + QgsProviderRegistry, + QgsProcessingModelChildParameterSource) from qgis.PyQt import uic from qgis.PyQt.QtCore import Qt from qgis.PyQt.QtCore import QByteArray @@ -105,8 +106,11 @@ class MultipleInputDialog(BASE, WIDGET): # add extra options (e.g. manually added layers) for t in [o for o in self.selectedoptions if not isinstance(o, int)]: - item = QStandardItem(t) - item.setData(t, Qt.UserRole) + if isinstance(t, QgsProcessingModelChildParameterSource): + item = QStandardItem(t.staticValue()) + else: + item = QStandardItem(t) + item.setData(item.text(), Qt.UserRole) item.setCheckState(Qt.Checked) item.setCheckable(True) self.model.appendRow(item) diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index f38174935aa..db431077aca 100644 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -69,6 +69,7 @@ from qgis.core import ( QgsProcessingOutputVectorLayer, QgsProcessingOutputString, QgsProcessingOutputNumber, + QgsProcessingModelChildParameterSource, QgsProcessingModelAlgorithm) from qgis.PyQt.QtWidgets import ( @@ -562,7 +563,7 @@ class MultipleInputWidgetWrapper(WidgetWrapper): return widget else: options = [self.dialog.resolveValueDescription(opt) for opt in self._getOptions()] - return MultipleInputPanel(options) + return MultipleInputPanel(options, datatype=self.param.layerType()) def refresh(self): if self.param.layerType() != QgsProcessing.TypeFile: @@ -582,15 +583,20 @@ class MultipleInputWidgetWrapper(WidgetWrapper): return self.widget.setText(value) else: options = self._getOptions() - selected = [] - for i, opt in enumerate(options): - try: - if opt in value: - selected.append(i) - except TypeError: - if opt == value: - selected.append(i) - self.widget.setSelectedItems(selected) + + if not isinstance(value, (tuple, list)): + value = [value] + + selected_options = [] + for sel in value: + if sel in options: + selected_options.append(options.index(sel)) + elif isinstance(sel, QgsProcessingModelChildParameterSource): + selected_options.append(sel.staticValue()) + else: + selected_options.append(sel) + + self.widget.setSelectedItems(selected_options) def value(self): if self.dialogType == DIALOG_STANDARD: @@ -608,7 +614,7 @@ class MultipleInputWidgetWrapper(WidgetWrapper): return self.widget.getText() else: options = self._getOptions() - values = [options[i] for i in self.widget.selectedoptions] + values = [options[i] if isinstance(i, int) else QgsProcessingModelChildParameterSource.fromStaticValue(i) for i in self.widget.selectedoptions] if len(values) == 0 and not self.param.flags() & QgsProcessing.FlagOptional: raise InvalidParameterValue() return values From f88ddcb5439b98aad26f143f23d53e14a91e6369 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 7 Aug 2017 07:27:38 +1000 Subject: [PATCH 17/19] Remove startup tips --- src/app/CMakeLists.txt | 4 - src/app/qgisapp.cpp | 13 -- src/app/qgsoptions.cpp | 2 - src/app/qgstip.h | 59 ------- src/app/qgstipfactory.cpp | 362 -------------------------------------- src/app/qgstipfactory.h | 77 -------- src/app/qgstipgui.cpp | 108 ------------ src/app/qgstipgui.h | 43 ----- src/ui/qgsoptionsbase.ui | 106 +++-------- 9 files changed, 28 insertions(+), 746 deletions(-) delete mode 100644 src/app/qgstip.h delete mode 100644 src/app/qgstipfactory.cpp delete mode 100644 src/app/qgstipfactory.h delete mode 100644 src/app/qgstipgui.cpp delete mode 100644 src/app/qgstipgui.h diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 47d4be21d31..14d10cde980 100755 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -124,8 +124,6 @@ SET(QGIS_APP_SRCS qgstextannotationdialog.cpp qgssvgannotationdialog.cpp qgsundowidget.cpp - qgstipgui.cpp - qgstipfactory.cpp qgsvectorlayerproperties.cpp qgsmapthemes.cpp qgshandlebadlayers.cpp @@ -308,8 +306,6 @@ SET (QGIS_APP_MOC_HDRS qgsstatisticalsummarydockwidget.h qgssvgannotationdialog.h qgstextannotationdialog.h - qgstipgui.h - qgstipfactory.h qgsundowidget.h qgsvectorlayerproperties.h qgsmapthemes.h diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index dcc5b72cfc2..25fc2159578 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -261,7 +261,6 @@ Q_GUI_EXPORT extern int qt_defaultDpiX(); #include "qgstaskmanagerwidget.h" #include "qgssymbolselectordialog.h" #include "qgstextannotation.h" -#include "qgstipgui.h" #include "qgsundowidget.h" #include "qgsuserinputdockwidget.h" #include "qgsvectordataprovider.h" @@ -1168,18 +1167,6 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh toggleSnapping->setProperty( "Icon", QgsApplication::getThemeIcon( "/mIconSnapping.svg" ) ); connect( toggleSnapping, &QShortcut::activated, mSnappingUtils, &QgsSnappingUtils::toggleEnabled ); - // Show a nice tip of the day - if ( settings.value( QStringLiteral( "qgis/showTips%1" ).arg( Qgis::QGIS_VERSION_INT / 100 ), true ).toBool() ) - { - mSplash->hide(); - QgsTipGui myTip( this ); - myTip.exec(); - } - else - { - QgsDebugMsg( "Tips are disabled" ); - } - if ( ! QTouchDevice::devices().isEmpty() ) { //add reacting to long click in touch diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp index 19e5f9db503..5f76ac7af2c 100644 --- a/src/app/qgsoptions.cpp +++ b/src/app/qgsoptions.cpp @@ -628,7 +628,6 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QListsetChecked( mSettings->value( QStringLiteral( "/qgis/legendLayersBold" ), true ).toBool() ); mLegendGroupsBoldChkBx->setChecked( mSettings->value( QStringLiteral( "/qgis/legendGroupsBold" ), false ).toBool() ); cbxHideSplash->setChecked( mSettings->value( QStringLiteral( "/qgis/hideSplash" ), false ).toBool() ); - cbxShowTips->setChecked( mSettings->value( QStringLiteral( "/qgis/showTips%1" ).arg( Qgis::QGIS_VERSION_INT / 100 ), true ).toBool() ); mDataSourceManagerNonModal->setChecked( mSettings->value( "/qgis/dataSourceManagerNonModal", false ).toBool() ); cbxCheckVersion->setChecked( mSettings->value( QStringLiteral( "/qgis/checkVersion" ), true ).toBool() ); cbxAttributeTableDocked->setChecked( mSettings->value( QStringLiteral( "/qgis/dockAttributeTable" ), false ).toBool() ); @@ -1209,7 +1208,6 @@ void QgsOptions::saveOptions() bool legendGroupsBold = mSettings->value( QStringLiteral( "/qgis/legendGroupsBold" ), false ).toBool(); mSettings->setValue( QStringLiteral( "/qgis/legendGroupsBold" ), mLegendGroupsBoldChkBx->isChecked() ); mSettings->setValue( QStringLiteral( "/qgis/hideSplash" ), cbxHideSplash->isChecked() ); - mSettings->setValue( QStringLiteral( "/qgis/showTips%1" ).arg( Qgis::QGIS_VERSION_INT / 100 ), cbxShowTips->isChecked() ); mSettings->setValue( QStringLiteral( "/qgis/dataSourceManagerNonModal" ), mDataSourceManagerNonModal->isChecked() ); mSettings->setValue( QStringLiteral( "/qgis/checkVersion" ), cbxCheckVersion->isChecked() ); mSettings->setValue( QStringLiteral( "/qgis/dockAttributeTable" ), cbxAttributeTableDocked->isChecked() ); diff --git a/src/app/qgstip.h b/src/app/qgstip.h deleted file mode 100644 index 7fcd57ce1b3..00000000000 --- a/src/app/qgstip.h +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************** - qgstip.h - --------------------- - begin : February 2011 - copyright : (C) 2011 by Tim Sutton - email : tim at linfiniti 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 QGSTIP -#define QGSTIP - -#include -#include -#include "qgis_app.h" - -/** \ingroup app -* \brief An QgsTip represents a tip generated by the -* QgsTipFactory factory class to serve up tips to the user. -* Tips can be generic, in which case they make no mention of -* gui dialogs etc, or gui-specific in which case they may allude -* to features of the graphical user interface. -* \see also QgsTipOfTheDay, QgsTipFactory -*/ - -class APP_EXPORT QgsTip -{ - public: - //! Constructor - QgsTip() {} - //! Destructor - ~QgsTip() {} - // - // Accessors - // - //! Get the tip title - QString title() {return mTitle;} - //! Get the tip content - QString content() {return mContent;} - - // - // Mutators - // - //! Set the tip title - void setTitle( const QString &title ) {mTitle = title;} - //! Set the tip content - void setContent( const QString &content ) {mContent = content;} - private: - QString mTitle; - QString mContent; -}; - -#endif //QGSTIP - diff --git a/src/app/qgstipfactory.cpp b/src/app/qgstipfactory.cpp deleted file mode 100644 index 660e7f8e39f..00000000000 --- a/src/app/qgstipfactory.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/*************************************************************************** - qgstipfactory.cpp - --------------------- - begin : February 2011 - copyright : (C) 2007 by Tim Sutton - email : tim at linfiniti 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 "qgstipfactory.h" -#include -//for rand & srand -#include - - -QgsTipFactory::QgsTipFactory() : QObject() -{ - // I'm just doing this in a simple way so - // its easy for translators...later - // it its worth the time I'll move this data - // into a sqlite database... - QgsTip myTip; - myTip.setTitle( tr( "QGIS is open source" ) ); - myTip.setContent( tr( "QGIS is open source software." - " This means that the software source code can be freely viewed" - " and modified. The GPL places a restriction that any modifications" - " you make must be made available in source form to whoever you give" - " modified versions to, and that you can not create a new version of" - " QGIS under a 'closed source' license. Visit" - " the QGIS home page" - " for more information." - ) ); - addGenericTip( myTip ); - // - myTip.setTitle( tr( "QGIS Publications" ) ); - myTip.setContent( tr( "If you write a scientific paper or any other article" - " that refers to QGIS we would love to include your work" - " in the case studies section of" - " the QGIS home page." - ) ); - addGenericTip( myTip ); - myTip.setTitle( tr( "Become a QGIS translator" ) ); - myTip.setContent( tr( "Would you like to see QGIS" - " in your native language? We are looking for more translators" - " and would appreciate your help! The translation process is" - " fairly straight forward - instructions are available in the" - " QGIS wiki" - " translator's page." - ) ); - addGuiTip( myTip ); - myTip.setTitle( tr( "Getting Help With QGIS" ) ); - myTip.setContent( tr( "If you need help using QGIS" - " there is a 'users' mailing list where users help each other with issues" - " related to using QGIS. We also have a 'developers' mailing list" - " for those wanting help and discuss things relating to the QGIS code base." - " Details on different means to get help are described in the" - " community section of the QGIS home page." - ) ); - addGuiTip( myTip ); - myTip.setTitle( tr( "Is it 'QGIS' or 'Quantum GIS'?" ) ); - myTip.setContent( tr( "Both used to be correct, but we recently decided to just use 'QGIS'. For articles we suggest you write 'QGIS is ....'" - ) ); - addGenericTip( myTip ); - myTip.setTitle( tr( "How do I refer to QGIS?" ) ); - myTip.setContent( tr( "QGIS is spelled in all caps." - " We have various subprojects of the QGIS project" - " and it will help to avoid confusion if you refer to each by" - " its name:" - "
    " - "
  • QGIS Library - this is the C++ library that contains" - " the core logic that is used to build the QGIS user interface and other applications.
  • " - "
  • QGIS Desktop - this is the desktop application that you know and love so much :-).
  • " - "
  • QGIS Server - this is a server-side application based on the QGIS Library" - " that will serve up your .qgs projects using OGC standard protocols.
  • " - "
" - ) ); - addGenericTip( myTip ); - // This tip contributed by Andreas Neumann - myTip.setTitle( tr( "Add the current date to a map layout" ) ); - myTip.setContent( tr( "You can add a current date variable to your map" - " layout. Create a regular text label and add the string" - " $CURRENT_DATE(yyyy-MM-dd) to the text box. See the" - " " - "QDate::toString format documentation for the possible date formats." - ) ); - addGuiTip( myTip ); - myTip.setTitle( tr( "Moving Elements and Maps in the Print Composer" ) ); - myTip.setContent( tr( "In the print composer toolbar you can find two buttons for moving" - " elements. The first one ( )" - " selects and moves elements in the layout. After selecting the element" - " with this tool you can also move them around with the arrow keys." - " For accurate positioning use the %1 section," - " which can be found in the tab %2." - " The other move tool ( )" - " allows you to move the map content within a map frame." ) - .arg( tr( "Position and Size" ) ) - .arg( tr( "Item Properties" ) ) - ); - addGuiTip( myTip ); - addGuiTip( myTip ); - // This tip contributed by Andreas Neumann - myTip.setTitle( tr( "Lock an item in the layout view" ) ); - myTip.setContent( tr( "Locking an element in the layout view prevents you to select or accidentally" - " move it with the mouse. Locking an item is done by checking its" - " state in the" - " %1 tab. While in a locked state, you can still get it" - " selected from the %1 tab, and configure any of its" - " properties in the %2 tab, including precisely setting" - " its position and size." ) - .arg( tr( "Items" ) ) - .arg( tr( "Item Properties" ) ) - ); - addGuiTip( myTip ); - // This tip contributed by Andreas Neumann - myTip.setTitle( tr( "Rotating a map and linking a north arrow" ) ); - myTip.setContent( tr( "In the Print Composer you can rotate a map by setting its rotation value" - " in the tab Item Properties -> Map -> Main properties section." - " To place a north arrow in your layout you can use the" - " %1 tool. After the selection and" - " placement of the north arrow in the layout you can link it" - " with a specific map frame by activating the %2" - " checkbox and selecting a map frame. Whenever you change the rotation" - " value of a linked map, the north arrow will now automatically adjust" - " its rotation." ) - .arg( tr( "Add Image" ) ) - .arg( tr( "Sync with map" ) ) - ); - addGuiTip( myTip ); - addGuiTip( myTip ); - // This tip contributed by Andreas Neumann - myTip.setTitle( tr( "Numeric scale value in map layout linked to map frame" ) ); - myTip.setContent( tr( "If you want to place a text label as a placeholder for the" - " current scale, linked to a map frame, you need to place a scalebar and" - " set the style to 'Numeric'. You also need to select the map frame, if there" - " is more than one." - ) ); - addGuiTip( myTip ); - // by Tim - myTip.setTitle( tr( "Using the mouse scroll wheel" ) ); - myTip.setContent( tr( "You can use the scroll wheel on your mouse to zoom in," - " out and pan the map. Scroll forwards to zoom in, scroll backwards to" - " zoom out and press and hold the scroll wheel down to pan the map. You" - " can configure the zoom scale factor in the %1 -> %2 panel." ) - .arg( tr( "Options" ) ) - .arg( tr( "Map tools" ) ) - ); - addGuiTip( myTip ); - addGuiTip( myTip ); - // by Tim - myTip.setTitle( tr( "Disabling rendering" ) ); - myTip.setContent( tr( "Sometimes you have a very large dataset which takes ages" - " to draw. If you are going to be performing several" - " actions (e.g. modifying symbology options) and wish to temporarily" - " disable map rendering while you do so, you can uncheck the 'Render'" - " checkbox in the bottom right of the status bar. Don't forget to check" - " it on again when you are ready to have the map draw itself again!" - ) ); - addGuiTip( myTip ); - // Tip contributed by Alister Hood - myTip.setTitle( tr( "Join intersected polylines when rendering" ) ); - myTip.setContent( tr( "When applying layered styles to a polyline layer, you can join" - " intersecting lines together simply by enabling symbol levels." - " The image below shows a before (left) and after (right) view of" - " an intersection when symbol levels are enabled." ) + - QStringLiteral( "

" ) - ); - addGuiTip( myTip ); - // by Tim - myTip.setTitle( tr( "Auto-enable on the fly projection" ) ); - myTip.setContent( tr( "In the options dialog, under the CRS tab, you can set QGIS so that" - " whenever you create a new project, 'on the fly projection' is enabled" - " automatically and a pre-selected Coordinate Reference System of your" - " choice is used." - ) ); - addGuiTip( myTip ); - // by Tim - myTip.setTitle( tr( "Sponsor QGIS" ) ); - myTip.setContent( tr( "If QGIS is saving you money or you like our work and" - " have the financial ability to help, please consider sponsoring the" - " development of QGIS. We use money from sponsors to pay for" - " travel and costs related to our regular hackfest meetings, and to generally" - " support the goals of our project. Please see the QGIS Sponsorship Web" - " Page for more details." - ) ); - addGenericTip( myTip ); - // by gsherman - myTip.setTitle( tr( "QGIS has Plugins!" ) ); - myTip.setContent( tr( "QGIS has plugins that extend its functionality." - " QGIS ships with some core plugins you can explore from the" - " %1 -> %2 menu. In addition there" - " are a lot of Python plugins " - " contributed by the user community that can be" - " installed via this same menu. Don't miss out on all QGIS has to offer!" - " Check out the plugins and see what they can do for you." ) - .arg( tr( "Plugins" ) ) - .arg( tr( "Manage and Install Plugins..." ) ) - ); - addGuiTip( myTip ); - addGenericTip( myTip ); - // by yjacolin - myTip.setTitle( tr( "Add an action to layer" ) ); - myTip.setContent( tr( "Action in a layer allows user to trigger action when clicking on a geometry" - " with 'Run Feature Action' tools." - "For example, you can open a HTML page using the field value of the geometry " - "as a parameter. Look at the documentation." - ) ); - addGuiTip( myTip ); - // by yjacolin - myTip.setTitle( tr( "Copy, paste and cut in QGIS" ) ); - myTip.setContent( tr( "Copy, paste, and cut work as in another applications in QGIS. Select a " - "feature (a geometry or an attribute row in the attribute table) and use " - "one of these shortcuts: Ctrl+C to copy, Ctrl+X to cut, and Ctrl+V to paste." - ) ); - addGuiTip( myTip ); - // by yjacolin - myTip.setTitle( tr( "Right click with identify tools" ) ); - myTip.setContent( tr( "Right click with the identify tool to show a context menu from which you can " - "choose the layer in which to identify a feature. A sub-menu will list features " - "identified and a third sub-menu will show the action link setup for the layer. " - "If one of this sub-menu doesn't contain any information, the next sub-menu " - "will appear instead. For example, if you have just one layer, and click " - "somewhere with several features, the first menu will list the features " - "instead of layer list." - ) ); - addGuiTip( myTip ); - // by Alister Hood - myTip.setTitle( tr( "Use VRT files" ) ); - myTip.setContent( tr( "If you have a number of aerial photos spread across a wide area, instead of " - "loading each file as a separate layer you can treat them all as a single layer " - "by using a .vrt file. " - "To create a .vrt, go to %1 -> %2 -> %3." ) - .arg( tr( "Raster" ) ) - .arg( tr( "Miscellaneous" ) ) - .arg( tr( "Build Virtual Raster (Catalog)" ) ) - ); - addGuiTip( myTip ); - // by Harrissou Sant-anna - myTip.setTitle( tr( "Switch quickly between different styles of the layer" ) ); - myTip.setContent( tr( "From the Layer properties dialog, use the Styles -> Add combobox" - " to create as many combinations of layer properties settings (symbology, labeling," - " diagram, fields form, actions...) as you want. Then, simply switch between styles" - " from the context menu of the layer in %1 to automatically" - " get different custom representations of your data." ) - .arg( tr( "Layers Panel" ) ) - ); - addGuiTip( myTip ); - // by Harrissou Sant-anna - myTip.setTitle( tr( "Live update rendering" ) ); - myTip.setContent( tr( "Press F7 to activate the %1 panel from which you can" - " easily and quickly configure the layer rendering. Check the %2" - " option to automatically apply to the map canvas each of your modifications." ) - .arg( tr( "Layer Styling" ) ) - .arg( tr( "Live update" ) ) - ); - addGuiTip( myTip ); - // by Harrissou Sant-anna - myTip.setTitle( tr( "Print or export a specific feature from an atlas composition" ) ); - myTip.setContent( tr( "If you want to print or export the composition of only one feature of the atlas," - " start the atlas preview, select the desired feature in the drop-down list" - " and click on Composer -> Print menu (or use Composer ->" - " Export... to any supported file format)." - ) ); - addGuiTip( myTip ); - // by Harrissou Sant-anna - myTip.setTitle( tr( "Start QGIS from command line" ) ); - myTip.setContent( tr( "QGIS can be launched from command line and supports a number of options. This can be" - " handy if you need to use QGIS with particular configurations such as custom" - " user profile or, without plugins... To get the list of the options," - " enter qgis --help on the command line." - ) ); - addGuiTip( myTip ); - // by Harrissou Sant-anna - myTip.setTitle( tr( "Set your own shortcuts for your actions" ) ); - myTip.setContent( tr( "QGIS provides you with a list of predefined shortcuts you can use to speed" - " your workflow. These are available under %1 -> %2 " - " menu and can be extended and customized for any dialog or tool." ) - .arg( tr( "Settings" ) ) - .arg( tr( "Keyboard Shortcuts" ) ) - ); - addGuiTip( myTip ); - - /* Template for adding more tips - myTip.setTitle(tr("")); - myTip.setContent(tr("" - )); - addGuiTip(myTip); - */ -} - -QgsTipFactory::~QgsTipFactory() -{ - -} -//private helper method -void QgsTipFactory::addGuiTip( const QgsTip &tip ) -{ - mGuiTips << tip; - mAllTips << tip; -} -//private helper method -void QgsTipFactory::addGenericTip( const QgsTip &tip ) -{ - mGenericTips << tip; - mAllTips << tip; -} -QgsTip QgsTipFactory::getTip() -{ - int myRand = qrand(); - int myValue = static_cast( myRand % mAllTips.count() ); //range [0,(count-1)] - QgsTip myTip = mAllTips.at( myValue ); - return myTip; -} -QgsTip QgsTipFactory::getTip( int position ) -{ - QgsTip myTip = mAllTips.at( position ); - return myTip; -} -QgsTip QgsTipFactory::getGenericTip() -{ - int myRand = qrand(); - int myValue = static_cast( myRand % mGenericTips.count() ); //range [0,(count-1)] - QgsTip myTip = mGenericTips.at( myValue ); - return myTip; -} -QgsTip QgsTipFactory::getGuiTip() -{ - int myRand = qrand(); - int myValue = static_cast( myRand % mGuiTips.count() ); //range [0,(count-1)] - QgsTip myTip = mGuiTips.at( myValue ); - return myTip; -} - -int QgsTipFactory::randomNumber( int max ) -{ - Q_UNUSED( max ); - return 0; -} - -int QgsTipFactory::position( QgsTip tip ) -{ - for ( int i = 0; i < mAllTips.count(); ++i ) - { - QgsTip myTip = mAllTips.at( i ); - if ( myTip.title() == tip.title() ) - { - return i; - } - } - return -1; -} - -int QgsTipFactory::count() -{ - return mAllTips.count(); -} diff --git a/src/app/qgstipfactory.h b/src/app/qgstipfactory.h deleted file mode 100644 index 9c66aa80090..00000000000 --- a/src/app/qgstipfactory.h +++ /dev/null @@ -1,77 +0,0 @@ -/*************************************************************************** - qgstipfactory.h - --------------------- - begin : February 2011 - copyright : (C) 2011 by Tim Sutton - email : tim at linfiniti 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 QGSTIPFACTORY -#define QGSTIPFACTORY - -#include "qgstip.h" -#include -#include "qgis_app.h" - -/** \ingroup app -* \brief A factory class to serve up tips to the user. -* Tips can be generic, in which case they make no mention of -* gui dialogs etc, or gui-specific in which case they may allude -* to features of the graphical user interface. -* \see also QgsTipOfTheDay, QgsTip -*/ - -class APP_EXPORT QgsTipFactory : public QObject -{ - Q_OBJECT //used for tr() so we don't need to do QObject::tr() - public: - //! Constructor - QgsTipFactory(); - - ~QgsTipFactory(); - - /** Get a random tip (generic or gui-centric) - * \returns An QgsTip containing the tip - */ - QgsTip getTip(); - - /** Get a specific tip (generic or gui-centric). - * \param position The tip returned will be based on the - * number passed in as position. If the - * position is invalid, an empty string will be - * returned. - * \returns An QgsTip containing the tip - */ - QgsTip getTip( int position ); - - /** Get a random generic tip - * \returns An QgsTip containing the tip - */ - QgsTip getGenericTip(); - - /** Get a random gui-centric tip - * \returns An QgsTip containing the tip - */ - QgsTip getGuiTip(); - - int position( QgsTip ); - int count(); - - private: - void addGenericTip( const QgsTip & ); - void addGuiTip( const QgsTip & ); - int randomNumber( int max ); - //@TODO move tipts into a sqlite db - QList mGenericTips; - QList mGuiTips; - QList mAllTips; -}; -#endif //QGSTIPFACTORY - diff --git a/src/app/qgstipgui.cpp b/src/app/qgstipgui.cpp deleted file mode 100644 index bd511c0d20e..00000000000 --- a/src/app/qgstipgui.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/*************************************************************************** - qgstipgui.cpp - description - ------------------- - begin : Sat Aug 10 2002 - copyright : (C) 2002 by Gary E.Sherman - email : sherman at mrcc.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 - -#include "qgstipgui.h" -#include "qgssettings.h" -#include "qgsapplication.h" -#include "qgstip.h" -#include "qgstipfactory.h" - -#ifdef Q_OS_MACX -QgsTipGui::QgsTipGui( QWidget *parent ) - : QDialog( parent, Qt::WindowSystemMenuHint ) // Dialog with close button only -#else -QgsTipGui::QgsTipGui( QWidget * parent ) - : QDialog( parent ) // Normal dialog in non Mac-OS -#endif -{ - setupUi( this ); - init(); -} - -QgsTipGui::~QgsTipGui() -{ -} - -void QgsTipGui::init() -{ - - QgsTipFactory myFactory; - QgsTip myTip = myFactory.getTip(); - mTipPosition = myFactory.position( myTip ); - - showTip( myTip ); - - QPushButton *pb = nullptr; - pb = new QPushButton( tr( "&Previous" ) ); - connect( pb, &QAbstractButton::clicked, this, &QgsTipGui::prevClicked ); - buttonBox->addButton( pb, QDialogButtonBox::ActionRole ); - - pb = new QPushButton( tr( "&Next" ) ); - connect( pb, &QAbstractButton::clicked, this, &QgsTipGui::nextClicked ); - buttonBox->addButton( pb, QDialogButtonBox::ActionRole ); -} - -void QgsTipGui::showTip( QgsTip myTip ) -{ - // TODO - This html construction can be simplified using QStringBuilder - // once Qt 4.6 is the minimum required version for building QGIS. - // - QString content = "" - + "

" - + myTip.title() - + "


" - + myTip.content(); - - txtTip->setHtml( content ); -} - -void QgsTipGui::on_cbxDisableTips_toggled( bool flag ) -{ - QgsSettings settings; - //note the ! below as when the cbx is checked (true) we want to - //change the setting to false - settings.setValue( QStringLiteral( "/qgis/showTips%1" ).arg( Qgis::QGIS_VERSION_INT / 100 ), !flag ); - hide(); -} - -void QgsTipGui::nextClicked() -{ - mTipPosition += 1; - QgsTipFactory myFactory; - if ( mTipPosition >= myFactory.count() ) - { - mTipPosition = 0; - } - QgsTip myTip = myFactory.getTip( mTipPosition ); - showTip( myTip ); -} - -void QgsTipGui::prevClicked() -{ - mTipPosition -= 1; - QgsTipFactory myFactory; - if ( mTipPosition < 0 ) - { - mTipPosition = myFactory.count() - 1; - } - QgsTip myTip = myFactory.getTip( mTipPosition ); - showTip( myTip ); -} diff --git a/src/app/qgstipgui.h b/src/app/qgstipgui.h deleted file mode 100644 index fd62ea2e9ae..00000000000 --- a/src/app/qgstipgui.h +++ /dev/null @@ -1,43 +0,0 @@ -/*************************************************************************** - qgstipgui.h - description - ------------------- - begin : Fri 18 Feb 2011 - copyright : (C) 2011 by Tim Sutton - email : tim@linfiniti.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 QGSTIPGUI_H -#define QGSTIPGUI_H - -#include "ui_qgstipguibase.h" -#include "qgis_app.h" -class QgsTip; - -class APP_EXPORT QgsTipGui : public QDialog, private Ui::QgsTipGuiBase -{ - Q_OBJECT - public: - QgsTipGui( QWidget *parent = nullptr ); - ~QgsTipGui(); - - private: - void init(); - void showTip( QgsTip ); - - int mTipPosition; - - private slots: - void on_cbxDisableTips_toggled( bool flag ); - void prevClicked(); - void nextClicked(); -}; - -#endif diff --git a/src/ui/qgsoptionsbase.ui b/src/ui/qgsoptionsbase.ui index 4d1bf4b5a8c..c37595786e9 100755 --- a/src/ui/qgsoptionsbase.ui +++ b/src/ui/qgsoptionsbase.ui @@ -602,8 +602,32 @@ - - + + + 0 + + + + + Modeless data source manager dialog + + + + + + + QGIS-styled group boxes + + + + + + + Check QGIS version at startup + + + + @@ -616,82 +640,14 @@ - - - - - 12 - 0 - - - - Qt::Vertical - - - - - - - - 0 - 0 - - - - Show tips at start up - - - - - - - - 12 - 0 - - - - Qt::Vertical - - - - - - - Check QGIS version at startup - - - - - - - - - - - Modeless data source manager dialog - - - - - - - QGIS-styled group boxes - - - - - - - - + Use native color chooser dialogs - + Use live-updating color chooser dialogs @@ -5543,12 +5499,6 @@ The bigger the number, the faster zooming with the mouse wheel will be. mFontFamilyComboBox spinFontSize mMessageTimeoutSpnBx - cbxHideSplash - cbxShowTips - cbxCheckVersion - mCustomGroupBoxChkBx - mNativeColorDialogsChkBx - mLiveColorDialogsChkBx groupBox_11 mProjectOnLaunchCmbBx mProjectOnLaunchLineEdit From a7cef8708d62184b5383a1105c89ce4fae6b0d90 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Thu, 17 Aug 2017 07:55:30 +0200 Subject: [PATCH 18/19] add docker shield --- Dockerfile | 3 +++ README.md | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f9d68291f19..4623f87fb25 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,7 @@ +# CACHE_TAG is provided by Docker cloud +# see https://docs.docker.com/docker-cloud/builds/advanced/ +# using ARG in FROM requires min v17.05.0-ce ARG CACHE_TAG=latest FROM qgis/qgis3-build-deps:${CACHE_TAG} diff --git a/README.md b/README.md index 416dea14fbd..9cde4ff40be 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # About QGIS [![Build Status](https://travis-ci.org/qgis/QGIS.svg?branch=master)](https://travis-ci.org/qgis/QGIS) +[![Docker Status](https://img.shields.io/docker/automated/qgis/qgis.svg)](https://cloud.docker.com/app/qgis/repository/docker/qgis/qgis/general) QGIS is an Open Source Geographic Information System. The project was born in May of 2002 and was established as a project on SourceForge in June of the same @@ -63,7 +64,7 @@ an email address where we can request additional information. ## Support You can get support in the following ways: - - Using the QGIS community site at http://qgis.org + - Using the QGIS community site at http://qgis.org - Joining the [qgis-users mailing list](https://lists.osgeo.org/mailman/listinfo/qgis-user) - Using IRC by joining the [#qgis](http://webchat.freenode.net/?channels=#qgis) channel on irc.freenode.net. Please wait around for a response to your question as many folks on the channel are doing other things and it may take a while for them to notice your question. - Join the [Gitter](https://gitter.im/qgis/QGIS?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) chat. From e32589599ab6df2aeffc5ed1240380ed55934eec Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Thu, 17 Aug 2017 07:57:37 +0200 Subject: [PATCH 19/19] packaging updates after helpviewer removal (followup 55cdc89a1) --- debian/libqgis-dev.install.in | 3 ++- debian/qgis-common.install | 1 + debian/qgis.install | 1 - debian/qgis.lintian-overrides | 4 ---- ms-windows/osgeo4w/package.cmd | 1 - python/core/qgsapplication.sip | 6 ------ src/core/qgsapplication.cpp | 15 --------------- src/core/qgsapplication.h | 3 --- 8 files changed, 3 insertions(+), 31 deletions(-) delete mode 100644 debian/qgis.lintian-overrides diff --git a/debian/libqgis-dev.install.in b/debian/libqgis-dev.install.in index effa246be06..f28dcc3e0aa 100644 --- a/debian/libqgis-dev.install.in +++ b/debian/libqgis-dev.install.in @@ -3,7 +3,8 @@ usr/lib/libqgis_app.so usr/lib/libqgis_core.so usr/lib/libqgis_gui.so usr/lib/libqgis_analysis.so -#server#usr/lib/libqgis_server.so +usr/lib/libqgis_native.so +usr/lib/libqgis_server.so usr/lib/libqgisgrass{GRASSVER}.so usr/lib/libqgispython.so usr/share/qgis/FindQGIS.cmake diff --git a/debian/qgis-common.install b/debian/qgis-common.install index bd60a4ac005..c4e0539735d 100644 --- a/debian/qgis-common.install +++ b/debian/qgis-common.install @@ -12,6 +12,7 @@ usr/share/qgis/doc/images usr/share/qgis/doc/index.html usr/share/qgis/doc/news usr/share/qgis/doc/news.html +usr/share/qgis/doc/nohelp.html usr/share/qgis/doc/style.css usr/share/qgis/i18n/* usr/share/qgis/images/* diff --git a/debian/qgis.install b/debian/qgis.install index 3c2521bc80a..b45db9e3fcd 100644 --- a/debian/qgis.install +++ b/debian/qgis.install @@ -6,7 +6,6 @@ usr/lib/qgis/plugins/libspatialqueryplugin.so usr/lib/qgis/plugins/libofflineeditingplugin.so usr/lib/qgis/plugins/libtopolplugin.so usr/lib/qgis/plugins/libgeometrycheckerplugin.so -usr/lib/qgis/qgis_help usr/share/pixmaps/ usr/share/applications/ usr/share/mime/packages/ diff --git a/debian/qgis.lintian-overrides b/debian/qgis.lintian-overrides deleted file mode 100644 index 72759f00cb8..00000000000 --- a/debian/qgis.lintian-overrides +++ /dev/null @@ -1,4 +0,0 @@ -# Build uses -D_FORTIFY_SOURCE=2, but hardening-check reports: -# Fortify Source functions: no, only unprotected functions found! -# unprotected: fgets -qgis: hardening-no-fortify-functions usr/lib/qgis/qgis_help diff --git a/ms-windows/osgeo4w/package.cmd b/ms-windows/osgeo4w/package.cmd index add5b0242b8..b26db6dcef4 100644 --- a/ms-windows/osgeo4w/package.cmd +++ b/ms-windows/osgeo4w/package.cmd @@ -384,7 +384,6 @@ tar -C %OSGEO4W_ROOT% -cjf %ARCH%/release/qgis/%PACKAGENAME%/%PACKAGENAME%-%VERS "apps/%PACKAGENAME%/plugins/spatialqueryplugin.dll" ^ "apps/%PACKAGENAME%/plugins/topolplugin.dll" ^ "apps/%PACKAGENAME%/plugins/geometrycheckerplugin.dll" ^ - "apps/%PACKAGENAME%/qgis_help.exe" ^ "apps/%PACKAGENAME%/qtplugins/sqldrivers/qsqlspatialite.dll" ^ "apps/%PACKAGENAME%/qtplugins/designer/" ^ "apps/%PACKAGENAME%/python/" ^ diff --git a/python/core/qgsapplication.sip b/python/core/qgsapplication.sip index a6d1825cd78..8f051af9ceb 100644 --- a/python/core/qgsapplication.sip +++ b/python/core/qgsapplication.sip @@ -223,12 +223,6 @@ Returns the path to the licence file. :rtype: str %End - static QString helpAppPath(); -%Docstring -Returns the path to the help application. - :rtype: str -%End - static QString i18nPath(); %Docstring Returns the path to the translation directory. diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index 9228571278d..455ff6adfe3 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -614,21 +614,6 @@ QString QgsApplication::licenceFilePath() return ABISYM( mPkgDataPath ) + QStringLiteral( "/doc/LICENSE" ); } -QString QgsApplication::helpAppPath() -{ - QString helpAppPath; -#ifdef Q_OS_MACX - helpAppPath = applicationDirPath() + "/bin/qgis_help.app/Contents/MacOS"; -#else - helpAppPath = libexecPath(); -#endif - helpAppPath += QLatin1String( "/qgis_help" ); -#ifdef Q_OS_WIN - helpAppPath += ".exe"; -#endif - return helpAppPath; -} - QString QgsApplication::i18nPath() { if ( ABISYM( mRunningFromBuildDir ) ) diff --git a/src/core/qgsapplication.h b/src/core/qgsapplication.h index f9219ba39da..694e7b0a85a 100644 --- a/src/core/qgsapplication.h +++ b/src/core/qgsapplication.h @@ -234,9 +234,6 @@ class CORE_EXPORT QgsApplication : public QApplication */ static QString licenceFilePath(); - //! Returns the path to the help application. - static QString helpAppPath(); - //! Returns the path to the translation directory. static QString i18nPath();