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. 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/doc/api_break.dox b/doc/api_break.dox index 000f40309da..17e5773d269 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/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/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/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/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..07d9afe2fd3 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,27 @@ 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'))) + + 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, + 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(ParameterVrtDestination(self.OUTPUT, self.tr('Virtual'))) def name(self): return 'buildvirtualraster' @@ -80,25 +94,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)] diff --git a/python/plugins/processing/gui/MultipleInputDialog.py b/python/plugins/processing/gui/MultipleInputDialog.py index a8fd860f02d..222f9e556fe 100644 --- a/python/plugins/processing/gui/MultipleInputDialog.py +++ b/python/plugins/processing/gui/MultipleInputDialog.py @@ -29,12 +29,17 @@ __revision__ = '$Format:%H$' import os -from qgis.core import QgsSettings +from qgis.core import (QgsSettings, + QgsProcessing, + QgsVectorFileWriter, + QgsProviderRegistry, + QgsProcessingModelChildParameterSource) 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 +48,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 +75,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)) @@ -76,6 +88,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) @@ -83,15 +96,26 @@ 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)]: + 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) + + self.lstLayers.setModel(self.model) def accept(self): self.selectedoptions = [] @@ -106,15 +130,56 @@ 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) + + 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..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 ( @@ -524,20 +525,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)) @@ -546,7 +547,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,14 +556,14 @@ 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)) 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,11 +583,20 @@ class MultipleInputWidgetWrapper(WidgetWrapper): return self.widget.setText(value) else: options = self._getOptions() - selected = [] - for i, opt in enumerate(options): - if opt in 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: @@ -599,12 +609,12 @@ 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: 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 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)], 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/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 0cd83cafed0..dd2f575e3b2 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 @@ -342,8 +340,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 c6917bf7c5d..834f39f65f2 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" @@ -1181,18 +1180,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/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index db83a397888..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,33 +597,38 @@ 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(); + } + }; - if ( ( resultStringList.isEmpty() || resultStringList.at( 0 ).isEmpty() ) ) + processVariant( val ); + + if ( layers.isEmpty() && ( resultStringList.isEmpty() || resultStringList.at( 0 ).isEmpty() ) ) { resultStringList.clear(); // check default 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(); diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index a51531334ad..7806067f0d5 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -20,13 +20,18 @@ #include "qgsproject.h" #include "qgsvectorlayer.h" #include "qgsrasterlayer.h" +#include "qgsogrprovider.h" #include "qgsnewgeopackagelayerdialog.h" +#include "qgsmessageoutput.h" +#include "qgsvectorlayerexporter.h" #include #include #include #include +QGISEXTERN bool deleteLayer( const QString &uri, const QString &errCause ); + QgsDataItem *QgsGeoPackageDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem ) { QgsDebugMsg( "path = " + path ); @@ -268,6 +273,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 ) ) @@ -327,7 +441,6 @@ void QgsGeoPackageConnectionItem::addTable() QList QgsGeoPackageAbstractLayerItem::actions() { QList lst; - // TODO: delete layer when the provider supports it (not currently implemented) return lst; } @@ -352,3 +465,55 @@ QgsGeoPackageRasterLayerItem::QgsGeoPackageRasterLayerItem( QgsDataItem *parent, { } + + +#ifdef HAVE_GUI +QList QgsGeoPackageVectorLayerItem::actions() +{ + QList lst = QgsGeoPackageAbstractLayerItem::actions(); + QAction *actionDeleteLayer = new QAction( tr( "Delete layer '%1'..." ).arg( mName ), this ); + connect( actionDeleteLayer, &QAction::triggered, this, &QgsGeoPackageVectorLayerItem::deleteLayer ); + lst.append( actionDeleteLayer ); + return lst; +} + + +void QgsGeoPackageVectorLayerItem::deleteLayer() +{ + // Check if the layer is in the registry + const QgsMapLayer *projectLayer = nullptr; + Q_FOREACH ( const QgsMapLayer *layer, QgsProject::instance()->mapLayers() ) + { + 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::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/qgsgeopackagedataitems.h b/src/providers/ogr/qgsgeopackagedataitems.h index 3d47d4da921..d736cb4dded 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 }; @@ -67,7 +71,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 ); diff --git a/src/providers/ogr/qgsogrdataitems.cpp b/src/providers/ogr/qgsogrdataitems.cpp index 2f92da08872..6aef581b5c6 100644 --- a/src/providers/ogr/qgsogrdataitems.cpp +++ b/src/providers/ogr/qgsogrdataitems.cpp @@ -18,10 +18,12 @@ #include "qgslogger.h" #include "qgsmessagelog.h" #include "qgssettings.h" +#include "qgsproject.h" #include #include #include +#include #include #include @@ -31,11 +33,14 @@ 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 ) + 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 @@ -56,6 +61,7 @@ QgsOgrLayerItem::QgsOgrLayerItem( QgsDataItem *parent, } } + bool QgsOgrLayerItem::setCrs( const QgsCoordinateReferenceSystem &crs ) { if ( !( mCapabilities & SetCrs ) ) @@ -110,9 +116,71 @@ QString QgsOgrLayerItem::layerName() const return info.completeBaseName(); } +#ifdef HAVE_GUI +QList QgsOgrLayerItem::actions() +{ + QList lst; + // 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; +} + +void QgsOgrLayerItem::deleteLayer() +{ + // Messages are different for files and tables + QString title = mIsSubLayer ? QObject::tr( "Delete Layer" ) : QObject::tr( "Delete File" ); + // Check if the layer is in the registry + const QgsMapLayer *projectLayer = nullptr; + Q_FOREACH ( const QgsMapLayer *layer, QgsProject::instance()->mapLayers() ) + { + if ( layer->publicSource() == mUri ) + { + projectLayer = layer; + } + } + if ( ! projectLayer ) + { + 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 ); + } + else + { + QMessageBox::information( nullptr, title, mIsSubLayer ? tr( "Layer deleted successfully." ) : tr( "File deleted successfully." ) ); + if ( mParent ) + mParent->refresh(); + } + } + else + { + 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 + // ------- -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 ); @@ -166,7 +234,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 ); } // ---- @@ -189,7 +257,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 a81ff1ba58d..5edf1bdb0ea 100644 --- a/src/providers/ogr/qgsogrdataitems.h +++ b/src/providers/ogr/qgsogrdataitems.h @@ -24,11 +24,19 @@ 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; QString layerName() const override; + +#ifdef HAVE_GUI + QList actions() override; + public slots: + void deleteLayer(); +#endif + private: + bool mIsSubLayer; }; diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index e7dbdf088c1..c9f83520b7c 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 = QObject::tr( "Not enough data to deserialize" ); + break; + case OGRERR_NOT_ENOUGH_MEMORY: + errCause = QObject::tr( "Not enough memory" ); + break; + case OGRERR_UNSUPPORTED_GEOMETRY_TYPE: + errCause = QObject::tr( "Unsupported geometry type" ); + break; + case OGRERR_UNSUPPORTED_OPERATION: + errCause = QObject::tr( "Unsupported operation" ); + break; + case OGRERR_CORRUPT_DATA: + errCause = QObject::tr( "Corrupt data" ); + break; + case OGRERR_FAILURE: + errCause = QObject::tr( "Failure" ); + break; + case OGRERR_UNSUPPORTED_SRS: + errCause = QObject::tr( "Unsupported SRS" ); + break; + case OGRERR_INVALID_HANDLE: + errCause = QObject::tr( "Invalid handle" ); + break; + case OGRERR_NON_EXISTING_FEATURE: + errCause = QObject::tr( "Non existing feature" ); + break; + default: + case OGRERR_NONE: + errCause = QObject::tr( "Success" ); + break; + } + errCause = QObject::tr( "GDAL result code: %s" ).arg( errCause ); + return error == OGRERR_NONE; + } + // This should never happen: + errCause = QObject::tr( "Layer not found: %s" ).arg( uri ); + return false; +} 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 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" diff --git a/tests/src/core/testqgsprocessing.cpp b/tests/src/core/testqgsprocessing.cpp index 10915eaf1a1..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 ); @@ -2310,6 +2319,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 ) ) );